import { Reducer } from 'redux'; import { Draft, produce } from 'immer'; import { GenericActionLookup, GenericActionMapping } from './action'; export type ImmerReducer = (state: Draft, action?: A) => T; export type HandlerMap> = { [actionType in keyof A]?: ImmerReducer[actionType]> }; function isHandlerFunction>( handlerOrMap: HandlerMap | ImmerReducer ): handlerOrMap is ImmerReducer { if (typeof handlerOrMap === 'function') { return true; } return false; } export function createGenericReducer, AM = keyof GenericActionMapping>( initialState: T, handlerOrMap: HandlerMap | ImmerReducer[AM]> ): Reducer { return function reducer(state = initialState, action: GenericActionLookup[AM]): T { if (isHandlerFunction(handlerOrMap)) { return produce(state, draft => handlerOrMap(draft, action as GenericActionLookup[AM])); } else if (handlerOrMap.hasOwnProperty(action.type)) { const handler = (handlerOrMap as any)[action.type] as ImmerReducer[AM]>; return produce(state, draft => handler(draft, action)); } else { return state; } }; }