holy heck the action shortcuts work

main
Yanick Champoux 2023-03-07 10:33:05 -05:00
parent 1a2b2df9ac
commit 9ac3032a7f
3 changed files with 62 additions and 14 deletions

View File

@ -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<typeof createAction>;
type AggregateState<L> = L;
type Mutation<A extends ActionCreator = ActionCreator, S = any> = (
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<ActionArg>,
ReturnType<ActionArg>,
ActionType
>
: ActionCreatorWithoutPayload<ActionType>;
type ResolveActions<
A extends {
[key: string]: any;
},
> = {
[ActionType in keyof A]: ActionType extends string
? ResolveAction<ActionType, A[ActionType]>
: never;
};
type Mutation<A extends ActionCreator<any> = ActionCreator<any>, S = any> = (
state: S,
payload: ReturnType<A>['payload'],
action: ReturnType<A>,
@ -27,20 +56,22 @@ type Mutation<A extends ActionCreator = ActionCreator, S = any> = (
export default class Updux<
T_LocalState = Record<any, any>,
T_LocalActions = {},
T_LocalActions extends {
[actionType: string]: any;
} = {},
SUBDUXES extends Record<string, Dux> = {},
> {
#localInitial: T_LocalState;
#localActions: T_LocalActions;
#localMutations: Record<
string,
Mutation<ActionCreator, AggregateState<T_LocalState>>
Mutation<ActionCreator<any>, AggregateState<T_LocalState>>
> = {};
#subduxes: SUBDUXES;
#name: string;
#actions: AggregateActions<T_LocalActions, SUBDUXES>;
#actions: AggregateActions<ResolveActions<T_LocalActions>, SUBDUXES>;
constructor(
config: Partial<{
@ -81,10 +112,10 @@ export default class Updux<
}
// TODO force the actionCreator to be one of the actions?
mutation<A extends ActionCreator>(
mutation<A extends ActionCreator<any>>(
actionCreator: A,
mutation: Mutation<A, AggregateState<T_LocalState>>,
) {
this.#localMutations[actionCreator.type] = mutation;
this.#localMutations[(actionCreator as any).type] = mutation;
}
}

View File

@ -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<boolean>()),
},
});
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 },
});
});

View File

@ -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<string, string> = {};
for (const slice in subduxes) {