Skip to content

Instantly share code, notes, and snippets.

@seokju-na
Created July 4, 2019 15:12
Show Gist options
  • Save seokju-na/c098d1c569b08be36675e64bccb3a7a2 to your computer and use it in GitHub Desktop.
Save seokju-na/c098d1c569b08be36675e64bccb3a7a2 to your computer and use it in GitHub Desktop.
import Event from './Event';
export type ReducerHandler<E extends Event, State> = (state: State, event: E) => State;
type EventType<E> = E extends Event ? E['type'] : never;
type ExtractEvent<E extends Event, Type> = E extends { type: Type } ? E : never;
export default interface Reducer<E extends Event, State> {
initialState?: State;
ensureFirstEventTypeToBe?: EventType<E>;
handlers: {
[T in EventType<E>]?: ReducerHandler<ExtractEvent<E, T>, State>;
};
}
export type ReducerFn<State> = (events: Event[], state?: State) => State;
export function makeReducerFn<E extends Event, State>(reducer: Reducer<E, State>): ReducerFn<State> {
const {
initialState,
ensureFirstEventTypeToBe,
handlers,
} = reducer;
return (events: E[], state: State = initialState): State => {
if (ensureFirstEventTypeToBe !== undefined) {
if (state === undefined && events[0].type !== ensureFirstEventTypeToBe) {
throw new Error();
}
}
return events.reduce<State>((previousState, event) => {
const handler = handlers[event.type];
if (handler === undefined) {
return previousState;
}
return handler(previousState, event);
}, state);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment