Created
March 28, 2018 21:40
-
-
Save marty-wang/b07157d970d42fa45eb7297b0d43a312 to your computer and use it in GitHub Desktop.
strongly typed action handler and dispatcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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