import {Dispatch, Action, AnyAction, Middleware, MiddlewareAPI} from 'redux'; // Thunk is a type of thunk function. export type Thunk = ( dispatch: Dispatch, getState: () => S, context?: C, ) => Promise; export const ACTION_TYPE = '@@redux-dutiful-thunk/THUNK'; export type ActionType = typeof ACTION_TYPE; export type ThunkType = any | null; // ThunkAction is a type of a thunk action that is created by thunk action creators. export type ThunkAction = { readonly type: ActionType; readonly thunk: Thunk; readonly promise: Promise; readonly thunkType: T; }; export type AnyThunkAction = ThunkAction< any, AnyAction, any, R, T >; // thunk creates a thunk action from a given thunk function. // A null is set to its thunk type. export function thunk( f: Thunk, ): ThunkAction { return thunkAs(null, f); } // thunkAs creates a thunk action from a given thunk function and thunk type. export function thunkAs( thunkType: T, f: Thunk, ): ThunkAction { let _resolve: (r: R) => any; let _reject: (a: any) => any; const promise = new Promise((resolve, reject) => { _resolve = resolve; _reject = reject; }); return { type: ACTION_TYPE, thunk: (dispatch, getState, context) => { f(dispatch, getState, context) .then(_resolve) .catch(_reject); return promise; }, promise, thunkType, }; } // isThunkAction determines a given action is a thunk action or not. export function isThunkAction(action: AnyAction): action is AnyThunkAction { return action.type === ACTION_TYPE; } // createThunkMiddleware creates a Redux middleware. // Optionally you can specify a context that will be passed to thunk functions. export function createThunkMiddleware( context?: C, ): Middleware<{}, any, D> { return ({dispatch, getState}: MiddlewareAPI) => next => action => { if (action != null && isThunkAction(action)) { action.thunk(dispatch, getState, context); // Currently we return `action` without calling `next` like redux-thunk. // But this is just an action so we can pass it to `next` as well. return action; } return next(action); }; }