I'm trying to create a higher order function which recevies an object of functions. Each function receives a state and an action object which has two fields (payload can be optional). The object must contain functions named as the field type of an action:
type RequiredActionFields<P = {}> = {
type: string;
payload: P;
};
Here's the function implementation:
type SomeFunction<S, A> = (state: S, action: A) => void;
type ObjWithFunctions<S, A extends RequiredActionFields> = Record<
A["type"],
SomeFunction<S, A>
>;
const functionThatDoesThings = <S>(state: S, cb: (draft: S) => void) => {
// does domethings else
return state;
};
function createThing<S, A extends RequiredActionFields>(
functions: ObjWithFunctions<S, A>
) {
return (state: S, action: A) => {
const reducer = functions[action.type as keyof typeof functions];
if (!reducer) {
throw new Error(`Unhandled action type`);
}
const nextState = functionThatDoesThings(state, (draft) => {
reducer(draft, action);
});
return nextState;
};
}
And now I want to use this function like this:
type Action =
| { type: "something"; payload: { id: 1 } }
| { type: "another"; payload: { close: true } };
type State = {
entities: [];
open: boolean;
};
createThing<State, Action>({
something: (state, action) => {
// ERROR: I want this typing to be inferred automatically because of the object key 'something'
action.payload.id;
if (action.type === "something") {
// WORKING: like this
action.payload.id;
}
},
another: (state, action) => {
// ERROR: I want this typing to be inferred automatically
action.payload.close;
}
});
The problem I have is narrowing the type down to the function specified into the obejct of functions.
It's kind of hard to clearly explain my intent so I hope that the code shows what I'm trying to do. Here's a codesandbox to also try it out: https://codesandbox.io/s/nice-hermann-kiigqy?file=/src/index.ts:804-1325