diff --git a/src/actions.ts b/src/actions.ts index 74a05fb..269cc81 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -9,6 +9,22 @@ export interface IAction { meta?: Metadata; } +export type IThunk = ( + dispatch: (action: any) => any, + getState: () => any +) => any; + +export function createAction( + type: string, + payloadCreator: (...args: U) => Thunk +): (...args: U) => Thunk; + +export function createAction( + type: string, + payloadCreator: (...args: U) => Thunk, + metadataCreator?: (...args: U) => Metadata +): (...args: U) => Thunk; + export function createAction( type: string ): () => IAction; @@ -28,13 +44,22 @@ export function createAction( type: string, payloadCreator?: (...args: U) => Payload, metadataCreator?: (...args: U) => Metadata -): (...args: U) => IAction { +) { return Object.assign( - (...args: U) => ({ - type, - ...(payloadCreator && { payload: payloadCreator(...args) }), - ...(metadataCreator && { meta: metadataCreator(...args) }), - }), + (...args: U) => { + const payload = (payloadCreator && payloadCreator(...args)); + const meta = (metadataCreator && metadataCreator(...args)); + + if (typeof payload === 'function') { + return payload; + } + + return { + type, + ...(payload && { payload }), + ...(meta && { meta }), + }; + }, { toString: () => type } ); } diff --git a/test/actions.test.ts b/test/actions.test.ts index 2b29133..8842efd 100644 --- a/test/actions.test.ts +++ b/test/actions.test.ts @@ -4,7 +4,7 @@ import { createAsyncAction, onFulfilled, onPending, - onRejected + onRejected, } from '../src/actions'; describe('actions', () => { @@ -59,7 +59,6 @@ describe('actions', () => { const action = createAction(TYPE, () => undefined, () => ({ asdf: 1234 })); assert.deepEqual(action(), { type: TYPE, - payload: undefined, meta: { asdf: 1234 }, }); }); @@ -152,4 +151,40 @@ describe('actions', () => { }); }); }); + + describe('thunk', () => { + const getStateMock = () => 42; + const dispatchMock = (action: any) => { + if (typeof action === 'function') { + return action(dispatchMock, getStateMock); + } + return action; + }; + + it('should return a thunk function', () => { + const thunk = createAction( + 'THUNK', + (n: number) => (_, getState) => getState() + n + ); + assert(typeof thunk(1) === 'function'); + }); + + it('should dispatch thunk payload', () => { + const thunk = createAction( + 'THUNK', + (n: number) => (_, getState) => getState() + n + ); + const result = dispatchMock(thunk(1)); + assert.equal(result, 43); + }); + + it('should return a thunk with metadata', () => { + const thunk = createAction( + 'THUNK', + (n: number) => (_, getState) => getState() + n, + (n: number) => ({ n }) + ); + assert(typeof thunk(1) === 'function'); + }); + }); });