Skip to content

Instantly share code, notes, and snippets.

@marty-wang
Created March 28, 2018 21:40
Show Gist options
  • Save marty-wang/b07157d970d42fa45eb7297b0d43a312 to your computer and use it in GitHub Desktop.
Save marty-wang/b07157d970d42fa45eb7297b0d43a312 to your computer and use it in GitHub Desktop.
strongly typed action handler and dispatcher
import { Store } from 'src/store';
type TagWithKey<TagName extends string, T> = { [K in keyof T]: { [_ in TagName]: K } & { params: T[K] } };
type Unionize<T> = T[keyof T];
type ActionHandlers<TParams, TParamsKey extends keyof TParams> = {
[Key in TParamsKey]: (params: TParams[Key]) => void
};
type ActionHandlersTable<TActionParams> = ActionHandlers<TActionParams, keyof TActionParams>;
interface IActionHandler<TActionParams extends {}> {
readonly handlers: ActionHandlersTable<TActionParams>;
}
type ActionParamsA = {
Increase: {
amount: number;
};
Foo: {
bar: { x: boolean };
};
};
class ActionHandlerA implements IActionHandler<ActionParamsA> {
public readonly handlers: ActionHandlersTable<ActionParamsA> = null;
constructor(store: Store) {
this.handlers = {
Increase: ({ amount }) => {
store.count += amount;
},
Foo: _ => {}
};
}
}
type ActionParamsB = {
Decrease: {
amount: number;
};
Bar: null;
};
export class ActionHandlerB implements IActionHandler<ActionParamsB> {
public readonly handlers: ActionHandlersTable<ActionParamsB>;
constructor(store: Store) {
this.handlers = {
Decrease: params => {
store.count -= params.amount;
},
Bar: _ => {}
};
}
}
type ActionType<T> = T extends IActionHandler<infer U> ? Unionize<TagWithKey<'type', U>> : never;
const createDispatcher = <T extends IActionHandler<any>>(actionHandlers: T[]) => {
return (actionType: ActionType<T>): void => {
for (let i = 0; i < actionHandlers.length; i++) {
const handler = actionHandlers[i].handlers[actionType.type];
if (handler) {
return handler.call(null, actionType.params);
}
}
};
};
export const createActionDispatcher = (store: Store) =>
createDispatcher([new ActionHandlerA(store), new ActionHandlerB(store)]);
export type Dispatch = ReturnType<typeof createActionDispatcher>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment