improve typing of actions
This commit is contained in:
parent
a272ee0329
commit
ce666b75bb
@ -13,12 +13,12 @@ tasks:
|
||||
|
||||
test: jest
|
||||
|
||||
'check:prettier': prettier --check .
|
||||
'prettier': prettier --check .
|
||||
|
||||
'check:prettier:fix': prettier --write .
|
||||
'prettier:fix': prettier --write .
|
||||
|
||||
check:
|
||||
deps: [tsc, test, 'check:prettier']
|
||||
deps: [tsc, test, 'prettier']
|
||||
|
||||
'test:types': tsd
|
||||
docs:
|
||||
|
@ -136,7 +136,10 @@ export class Updux<
|
||||
if (typeof actionArg === 'function' && actionArg.type) {
|
||||
this.#actions[type] = actionArg;
|
||||
} else {
|
||||
this.#actions[type] = action(type, actionArg);
|
||||
const args = Array.isArray(actionArg)
|
||||
? actionArg
|
||||
: [actionArg];
|
||||
this.#actions[type] = action(type, ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,7 +229,7 @@ export class Updux<
|
||||
this.#reactions = [...this.#reactions, subscription];
|
||||
}
|
||||
|
||||
setAction(type, payloadFunc?: Function) {
|
||||
setAction(type, payloadFunc?: (...args: any) => any) {
|
||||
const theAction = action(type, payloadFunc);
|
||||
|
||||
this.#actions = { ...this.#actions, [type]: theAction };
|
||||
@ -404,7 +407,7 @@ export class Updux<
|
||||
|
||||
for (const action in this.actions) {
|
||||
store.dispatch[action] = (...args) => {
|
||||
return store.dispatch(this.actions[action](...args));
|
||||
return store.dispatch(this.actions[action](...(args as any)));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,18 @@ export type Action<T extends string = string, TPayload = any> = {
|
||||
|
||||
export type ActionGenerator<
|
||||
TType extends string = string,
|
||||
TPayloadGen = undefined
|
||||
TPayloadGen = (...args: any[]) => any
|
||||
> = {
|
||||
type: TType;
|
||||
} & (TPayloadGen extends (...args: any) => any
|
||||
? (...args: Parameters<TPayloadGen>) => {
|
||||
type: TType;
|
||||
payload: ReturnType<TPayloadGen>;
|
||||
}
|
||||
? ReturnType<TPayloadGen> extends void
|
||||
? (...args: Parameters<TPayloadGen>) => {
|
||||
type: TType;
|
||||
}
|
||||
: (...args: Parameters<TPayloadGen>) => {
|
||||
type: TType;
|
||||
payload: ReturnType<TPayloadGen>;
|
||||
}
|
||||
: (...args: any[]) => { type: TType; payload?: unknown });
|
||||
|
||||
// interface Action {
|
||||
@ -25,7 +29,14 @@ export type ActionGenerator<
|
||||
// ): ActionGenerator<T, F>;
|
||||
// }
|
||||
|
||||
export function action(type, payloadFunction = null) {
|
||||
export function action<
|
||||
TType extends string,
|
||||
TPayload extends (...args: any) => any
|
||||
>(
|
||||
type: TType,
|
||||
payloadFunction?: TPayload,
|
||||
transformer?: Function
|
||||
): ActionGenerator<TType, TPayload> {
|
||||
const generator = function (...payloadArg) {
|
||||
const result: Action = { type };
|
||||
|
||||
@ -40,5 +51,9 @@ export function action(type, payloadFunction = null) {
|
||||
|
||||
generator.type = type;
|
||||
|
||||
return generator;
|
||||
return (
|
||||
transformer
|
||||
? (...args: any) => transformer(generator(...args), args)
|
||||
: generator
|
||||
) as any;
|
||||
}
|
||||
|
42
src/types.test.ts
Normal file
42
src/types.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Updux } from './Updux';
|
||||
import { Action, action, ActionGenerator } from './actions';
|
||||
import { expectAssignable, expectType, expectNotType } from 'tsd';
|
||||
|
||||
type SimplePayload = (x: number) => number;
|
||||
|
||||
test('dux actions', () => {
|
||||
const a1 = action('a1', (x: number) => x);
|
||||
|
||||
const inner = new Updux({
|
||||
actions: {
|
||||
a7: (x: string) => x,
|
||||
},
|
||||
});
|
||||
|
||||
const dux = new Updux({
|
||||
actions: {
|
||||
a1,
|
||||
a2: () => {},
|
||||
a5: (x: number) => x,
|
||||
a6: action('a6', undefined, (x) => ({
|
||||
...x,
|
||||
meta: 'bob',
|
||||
})),
|
||||
},
|
||||
subduxes: {
|
||||
inner,
|
||||
},
|
||||
});
|
||||
|
||||
expectAssignable<ActionGenerator<'a1', SimplePayload>>(dux.actions.a1);
|
||||
expectAssignable<ActionGenerator<'a5', SimplePayload>>(dux.actions.a5);
|
||||
expectAssignable<Action<'a2'>>(dux.actions.a2());
|
||||
|
||||
expectType<{ payload: number }>(dux.actions.a1(12));
|
||||
|
||||
expectType<{ type: 'a2' }>(dux.actions.a2());
|
||||
|
||||
expectType<{ type: 'a6' }>(dux.actions.a6());
|
||||
|
||||
expectType<{ type: 'a7'; payload: string }>(dux.actions.a7('potato'));
|
||||
});
|
31
src/types.ts
31
src/types.ts
@ -1,14 +1,14 @@
|
||||
import { Action } from './actions';
|
||||
import { Action, ActionGenerator } from './actions';
|
||||
|
||||
export type Dict<T> = Record<string, T>;
|
||||
|
||||
export type Upreducer<TState = any, TAction = Action> = (
|
||||
action: Action
|
||||
action: TAction
|
||||
) => (state: TState) => TState;
|
||||
|
||||
export type Reducer<TState = any, TAction = Action> = (
|
||||
state: TState | undefined,
|
||||
action: Action
|
||||
action: TAction
|
||||
) => TState;
|
||||
|
||||
export type UnionToIntersection<U> = (
|
||||
@ -22,11 +22,7 @@ type Subdux<TState = any> = {
|
||||
};
|
||||
|
||||
type StateOf<D> = D extends { initial: infer I } ? I : unknown;
|
||||
type ActionsOf<C> = C extends { actions: infer A }
|
||||
? {
|
||||
[K in keyof A]: Function;
|
||||
}
|
||||
: {};
|
||||
type ActionsOf<C> = C extends { actions: infer A } ? A : {};
|
||||
|
||||
type Subduxes = Record<string, Subdux>;
|
||||
|
||||
@ -44,5 +40,20 @@ type DuxActionsSubduxes<C> = C extends object ? ActionsOf<C[keyof C]> : unknown;
|
||||
|
||||
export type ItemsOf<C> = C extends object ? C[keyof C] : unknown;
|
||||
|
||||
export type AggregateDuxActions<TActions, TSubduxes> = TActions &
|
||||
UnionToIntersection<ActionsOf<ItemsOf<TSubduxes>>>;
|
||||
type Actionize<TType extends string, TArgs> = TArgs extends ActionGenerator
|
||||
? TArgs
|
||||
: TArgs extends any[]
|
||||
? ActionGenerator<TType, TArgs[0]>
|
||||
: ActionGenerator<TType, TArgs>;
|
||||
|
||||
export type MergeDuxActions<TActions, TSubduxes> = UnionToIntersection<
|
||||
ActionsOf<TSubduxes[keyof TSubduxes]> | TActions
|
||||
>;
|
||||
|
||||
export type AggregateDuxActionsInner<TActions> = {
|
||||
[K in keyof TActions]: K extends string ? Actionize<K, TActions[K]> : never;
|
||||
};
|
||||
|
||||
export type AggregateDuxActions<TActions, TSubduxes> = AggregateDuxActionsInner<
|
||||
MergeDuxActions<TActions, TSubduxes>
|
||||
>;
|
||||
|
Loading…
Reference in New Issue
Block a user