Earlier today, I posed a question about creating a mapping between redux action types and reducers to handle each type explicitly.
After receiving helpful guidance on how to create the mapping, I encountered an error when attempting to use it in creating a reducer.
Consider the following code snippet (it's lengthy, but intended as a minimal example):
export type IActionReducerMapping<S, A extends IReduxAction<string>> = {
[K in A['type']]: IReducer<S, Extract<A, { type: K }>>
};
interface IUser {
id: number;
name: string;
age: number;
}
interface IUserState {
[id: number]: IUser;
}
interface IAddUserAction {
type: 'ADD_USER';
payload: IUser;
}
interface ISetUserNameAction {
type: 'SET_USER_NAME';
payload: {
id: IUser['id'];
name: IUser['name'];
}
}
type UserAction = IAddUserAction | ISetUserNameAction;
const mapping: IActionReducerMapping<IUserState, UserAction> = {
'ADD_USER': (state, action) => ({
...state,
[action.payload.id]: action.payload,
}),
'SET_USER_NAME': (state, action) => ({
...state,
[action.payload.id]: {
...state[action.payload.id],
name: action.payload.name,
}
}),
};
const userReducer = (state: IUserState, action: UserAction) => mapping[action.type](state, action);
The error received is as follows:
Argument of type 'UserAction' is not assignable to parameter of type 'IAddUserAction & ISetUserNameAction'. Type 'IAddUserAction' is not assignable to type 'IAddUserAction & ISetUserNameAction'. Type 'IAddUserAction' is not assignable to type 'ISetUserNameAction'. Types of property 'type' are incompatible. Type '"ADD_USER"' is not assignable to type '"SET_USER_NAME"'.ts(2345)
I'm struggling to identify where the typing issue lies. Any insights on this would be greatly appreciated.
Playground link for reference.
Edit:
The following modified approach successfully resolves the issue:
const userReducer = (state: IUserState, action: ISetUserNameAction | IAddUserAction) => {
switch (action.type) {
case 'ADD_USER':
return mapping[action.type](state, action);
case 'SET_USER_NAME':
return mapping[action.type](state, action);
}
}
It appears that specifying the potential types upfront allows the compiler to understand the compatibility better. The automatic inference of this remains somewhat mysterious though.