diff --git a/src/actions.test.ts b/src/actions.test.ts new file mode 100644 index 0000000..7651c5a --- /dev/null +++ b/src/actions.test.ts @@ -0,0 +1,14 @@ +import { test, expect } from 'vitest'; + +import { action } from './actions.js'; + +test('basic action', () => { + const foo = action('foo', (thing: string) => ({ thing })); + + expect(foo('bar')).toEqual({ + type: 'foo', + payload: { + thing: 'bar', + }, + }); +}); diff --git a/src/actions.ts b/src/actions.ts new file mode 100644 index 0000000..c50eba8 --- /dev/null +++ b/src/actions.ts @@ -0,0 +1,51 @@ +export type Action = { + type: T; + meta?: Record; + payload?: TPayload; +}; + +export type ActionGenerator< + TType extends string = string, + TPayloadGen = (...args: any[]) => any, +> = { + type: TType; +} & (TPayloadGen extends (...args: any) => any + ? ReturnType extends void + ? (...args: Parameters) => { + type: TType; + } + : (...args: Parameters) => { + type: TType; + payload: ReturnType; + } + : (...args: any[]) => { type: TType; payload?: unknown }); + +export function action< + TType extends string, + TPayload extends (...args: any) => any, +>( + type: TType, + payloadFunction?: TPayload, + transformer?: Function, +): ActionGenerator { + let generator: any = function (...payloadArg:any[]) { + const result: Action = { type }; + + if (payloadFunction) { + result.payload = payloadFunction(...payloadArg); + } else { + if (payloadArg[0] !== undefined) result.payload = payloadArg[0]; + } + + return result; + }; + + if (transformer) { + const orig = generator; + generator = (...args: any) => transformer(orig(...args), args); + } + + generator.type = type; + + return generator; +}