I was trying to add function arguments overloads starting from the most specific but type narrowing doesn't seem to work. Also tried changing arguments to a union type, but type guards don't work either. What am I missing?
type IReducer<S, A> = (state: S, action: A) => S;
interface IAsyncHandlers<S, A extends IAction> {
request?: IReducer<S, A>;
success?: IReducer<S, A>;
failure?: IReducer<S, A & { payload: any; error: true }>;
}
interface IAction {
type: string;
payload: any;
}
const getActionHandler = <S, A>(handler?: IReducer<S, A>) => (state: S) => (action: A): S =>
handler ? handler(state, action) : state;
const handleAsyncAction = <S, A extends IAction>(handlers: IAsyncHandlers<S, A>): IReducer<S, A> => {
function reducer(state: S, action: A): S
function reducer(state: S, action: A & { error: true }): S;
function reducer(state: S, action: A & { meta: { isPending: true } }): S;
function reducer(state: S, action: A & { error?: any; meta?: any } ): S {
switch (true) {
case action.error:
// Property 'error' is optional in type 'IAction & { error?: any; meta?: any; }'
// but required in type '{ payload: any; error: true; }'.
return getActionHandler(handlers.failure)(state)(action);
case action.meta && action.meta.isPending:
return getActionHandler(handlers.request)(state)(action);
default:
return getActionHandler(handlers.success)(state)(action);
}
}
return reducer;
};