From 64782096df1baafd4583b1a9cc9134a854de9b1a Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Thu, 25 Aug 2022 14:19:45 -0400 Subject: [PATCH 1/3] add actions --- src/actions.test.ts | 14 +++++++++++++ src/actions.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/actions.test.ts create mode 100644 src/actions.ts 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; +} From 4d4a5dde50a33191b6d3cbed0eea29f55d0de782 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Thu, 25 Aug 2022 16:36:37 -0400 Subject: [PATCH 2/3] add actions --- src/Updux.ts | 31 ++++++++++++++++++++++++++++--- src/actions.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Updux.ts b/src/Updux.ts index 2ee4ea8..54e8aa0 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -1,15 +1,18 @@ import R from 'remeda'; +import { Action, ActionGenerator } from './actions'; /** * Configuration object typically passed to the constructor of the class Updux. */ -export interface UpduxConfig { +export interface UpduxConfig = Record, TSubduxes = {}> { /** * Local initial state. * @default {} */ initial?: TState; + actions?: TActions; + /** * Subduxes to be merged to this dux. */ @@ -22,13 +25,35 @@ export type DuxStateSubduxes = keyof C extends never ? unknown : { [K in keyof C]: StateOf }; -export class Updux { +// type UnionArrayTypes = A[number]; + +// type ActionType = A extends ActionGenerator ? B : never; + +// type ActionAsObject = Record< ActionType, A > }>; + +// type UnionToIntersection = +// (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never + +// type ActionsAsObject = ActionAsObject< A[number]> + +export class Updux { #localInitial: any = {}; #subduxes; + #actions : TActions; - constructor(config: UpduxConfig) { + constructor(config: UpduxConfig) { this.#localInitial = config.initial ?? {}; this.#subduxes = config.subduxes ?? {}; + + this.#actions = config.actions ?? ([] as any); + } + + get actions() { + return this.#actions; + } + + get action(): ActionsAsObject { + return Object.fromEntries(this.#actions.map( a => [ a.type, a ] )); } get initial(): TState & DuxStateSubduxes { diff --git a/src/actions.test.ts b/src/actions.test.ts index 7651c5a..16d20ae 100644 --- a/src/actions.test.ts +++ b/src/actions.test.ts @@ -2,6 +2,8 @@ import { test, expect } from 'vitest'; import { action } from './actions.js'; +import { Updux } from './Updux.js'; + test('basic action', () => { const foo = action('foo', (thing: string) => ({ thing })); @@ -12,3 +14,24 @@ test('basic action', () => { }, }); }); + +test( "Updux config accepts actions", () => { + const foo = new Updux({ + actions: { + one: action('one', (x: string) => ({x})), + two: action('two', x => x), + } + }); + + expect(Object.keys(foo.actions)).toHaveLength(2); + + expect( foo.actions.one ).toBeTypeOf('function'); + expect( foo.actions.one("potato") ).toEqual({ + type: 'one', + payload: { + x: 'potato' + } + }); + +} ) + From 9772ce4bec7120c951b455dec1193d2044e427a4 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Thu, 25 Aug 2022 16:44:19 -0400 Subject: [PATCH 3/3] cleanup --- src/Updux.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Updux.ts b/src/Updux.ts index 54e8aa0..5527ab2 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -25,17 +25,6 @@ export type DuxStateSubduxes = keyof C extends never ? unknown : { [K in keyof C]: StateOf }; -// type UnionArrayTypes = A[number]; - -// type ActionType = A extends ActionGenerator ? B : never; - -// type ActionAsObject = Record< ActionType, A > }>; - -// type UnionToIntersection = -// (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never - -// type ActionsAsObject = ActionAsObject< A[number]> - export class Updux { #localInitial: any = {}; #subduxes; @@ -52,10 +41,6 @@ export class Updux { - return Object.fromEntries(this.#actions.map( a => [ a.type, a ] )); - } - get initial(): TState & DuxStateSubduxes { if (Object.keys(this.#subduxes).length === 0) return this.#localInitial;