I've been tackling TypeScript and delving into code heavily influenced by the Redux way of working, (action, state) => state
.
I'm striving to use TypeScript in the strictest manner possible. The issue I'm encountering is best illustrated with an example.
Initially, I define an enum detailing the action types:
enum Categories {
CAT_A = 'categoryA',
CAT_B = 'categoryB'
}
Then, I outline distinct fields for each type:
type Data = {
[Categories.CAT_A]: {
foo: string;
},
[Categories.CAT_B]: {
bar: number;
}
}
Next, I establish my action types:
type Action<T extends Categories> = {
type: T;
id: string;
additionalField: boolean;
} & Data[T];
type AllActions = { [T in Categories]: Action<T> }[Categories]
Following that, here's the reducer:
type State = { somevar: string }; // just an example
const dataReducer = (action: AllActions, state: State): State => {
switch (action.type) {
case Categories.CAT_A:
action.foo; /* correct type */
break;
case Categories.CAT_B:
action.bar; /* correct type */
break;
}
return {...state};
}
So far, everything seems to be going smoothly!
In reality, dealing with numerous actions can make the switch
statement messy.
Hence, I experimented with this alternative:
const subReducers: { [K in Categories]: (action: Action<K>, state: State) => State } = {
[Categories.CAT_A]: (action, state) => {
// accurate action type here
return {...state};
},
[Categories.CAT_B]: (action, state) => ({...state})
}
This approach works wonders as most "subReducers" are concise. It also ensures I handle any future actions correctly.
However, when trying to write the actual reducer for this setup:
const reducer2 = (action: AllActions, state: State) => {
const subReducer = subReducers[action.type];
const newState = subReducer(action, state); //error
return newState;
}
The error here stems from the fact that action.type
isn't constrained to a specific type, causing ambiguity with subReducer
. While casting resolves the issue, I'm seeking a solution that doesn't necessitate it. Just as a thought exercise.
If anyone has ideas for a highly type-safe solution that allows me to split my reducer into smaller parts like in the aforementioned example, feel free to share.
Feel free to modify the type definitions or structure, but keep in mind that robust type safety and inference are key objectives here.