From 9ac3032a7f8eb937eca47752494d07e694a1a5b5 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Tue, 7 Mar 2023 10:33:05 -0500 Subject: [PATCH] holy heck the action shortcuts work --- src/Updux.ts | 47 +++++++++++++++++++++++++++++++++++++-------- src/actions.test.ts | 17 ++++++++++------ src/buildActions.ts | 12 ++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/Updux.ts b/src/Updux.ts index 3932ec8..20221a2 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -9,17 +9,46 @@ import { configureStore, Reducer, createAction, + ActionCreator, PrepareAction, + ActionCreatorWithPayload, + ActionCreatorWithoutPayload, + ActionCreatorWithPreparedPayload, } from '@reduxjs/toolkit'; import { withPayload } from './actions.js'; import { AggregateActions, Dux, UnionToIntersection } from './types.js'; import { buildActions } from './buildActions.js'; -type ActionCreator = ReturnType; - type AggregateState = L; -type Mutation = ( +type MyActionCreator = { type: string } & ((...args: any) => any); + +type F = null extends any ? 'u' : 'n'; + +type ResolveAction< + ActionType extends string, + ActionArg extends any, +> = ActionArg extends MyActionCreator + ? ActionArg + : ActionArg extends (...args: any) => any + ? ActionCreatorWithPreparedPayload< + Parameters, + ReturnType, + ActionType + > + : ActionCreatorWithoutPayload; + +type ResolveActions< + A extends { + [key: string]: any; + }, +> = { + [ActionType in keyof A]: ActionType extends string + ? ResolveAction + : never; +}; + +type Mutation = ActionCreator, S = any> = ( state: S, payload: ReturnType['payload'], action: ReturnType, @@ -27,20 +56,22 @@ type Mutation = ( export default class Updux< T_LocalState = Record, - T_LocalActions = {}, + T_LocalActions extends { + [actionType: string]: any; + } = {}, SUBDUXES extends Record = {}, > { #localInitial: T_LocalState; #localActions: T_LocalActions; #localMutations: Record< string, - Mutation> + Mutation, AggregateState> > = {}; #subduxes: SUBDUXES; #name: string; - #actions: AggregateActions; + #actions: AggregateActions, SUBDUXES>; constructor( config: Partial<{ @@ -81,10 +112,10 @@ export default class Updux< } // TODO force the actionCreator to be one of the actions? - mutation( + mutation>( actionCreator: A, mutation: Mutation>, ) { - this.#localMutations[actionCreator.type] = mutation; + this.#localMutations[(actionCreator as any).type] = mutation; } } diff --git a/src/actions.test.ts b/src/actions.test.ts index b74eb21..34651d0 100644 --- a/src/actions.test.ts +++ b/src/actions.test.ts @@ -101,17 +101,22 @@ test('throw if double action', () => { ).toThrow(/action 'foo' defined both in subduxes 'gamma' and 'beta'/); }); -test.todo('action definition shortcut', () => { +test('action definition shortcut', () => { const foo = new Updux({ actions: { - foo: undefined, - bar: (x) => ({ x }), + foo: 0, + bar: (x: number) => ({ x }), + baz: createAction('baz', withPayload()), }, }); - expect(foo.actions.foo('hello')).toEqual({ type: 'foo', payload: 'hello' }); - expect(foo.actions.bar('hello')).toEqual({ + expect(foo.actions.foo()).toEqual({ type: 'foo', payload: undefined }); + expect(foo.actions.baz(false)).toEqual({ + type: 'baz', + payload: false, + }); + expect(foo.actions.bar(2)).toEqual({ type: 'bar', - payload: { x: 'hello' }, + payload: { x: 2 }, }); }); diff --git a/src/buildActions.ts b/src/buildActions.ts index 21f0cef..bee0ebb 100644 --- a/src/buildActions.ts +++ b/src/buildActions.ts @@ -1,6 +1,18 @@ +import { createAction } from '@reduxjs/toolkit'; import * as R from 'remeda'; +import { withPayload } from './actions.js'; + +function resolveActions(configActions) { + return R.mapValues(configActions, (prepare, type: string) => { + if (typeof prepare === 'function' && prepare.type) return prepare; + + return createAction(type, withPayload(prepare)); + }); +} export function buildActions(localActions, subduxes) { + localActions = resolveActions(localActions); + let actions: Record = {}; for (const slice in subduxes) {