import {Reducer, ReducerAction, useReducer} from 'react'; type ReducersObject<S = any, A = any> = { [K in keyof S]: Reducer<S, A>; }; type ReducersState<M> = M extends ReducersObject ? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never; } : never; type ReducerFromReducersObject<M> = M extends { [P in keyof M]: infer R; } ? R extends Reducer<any, any> ? R : never : never; type AllReducerAction<M> = M extends ReducersObject ? ReducerAction<ReducerFromReducersObject<M>> : never; type CombinedState<S> = {} & S; export type CombinedReducer<M extends ReducersObject> = Reducer< CombinedState<ReducersState<M>>, AllReducerAction<M> >; export function makeCombinedReducers<M extends ReducersObject>( reducers: M ): CombinedReducer<M> { const keys: (keyof M)[] = Object.keys(reducers); return (state, action) => { const nextState = {} as ReducersState<M>; for (const key of keys) { nextState[key] = reducers[key](state[key], action); } return nextState; }; } export function useCombinedReducer<M extends ReducersObject>( reducers: M, initialState: CombinedState<ReducersState<M>> ): [CombinedState<ReducersState<M>>, React.Dispatch<AllReducerAction<M>>] { return useReducer(makeCombinedReducers(reducers), initialState); }