问题
I asked a question earlier today, based on my attempts to create a mapping between redux-action-types and reducers to handle each of those types, with the requirement that each action type is handled explicitly.
I received a great answer on how to do that, and I can now create such a mapping, but I'm getting an error when I try and use it to create a reducer.
If I have the following code (sorry, it's rather long, but I believe it is 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);
I get the following error:
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 can't see where I'm going wrong with the typing of these, could anyone point out where I've gone wrong?
Here's the playground link.
Edit:
The following works:
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);
}
}
Presumably because at this point the compiler is confident that it can know that the type of action and reducer will work nicely together. Still a mystery how I infer this automatically.
回答1:
I was able to solve this by creating a separate function to transform a mapping to a reducer:
export const reducerOf = <S, A extends IReduxAction<string>>(mapping: IActionReducerMapping<S, A>) =>
(state: S, action: A): S =>
mapping[action.type](state, action);
Now I can simply call it as:
const userReducer = reducerOf(mapping);
来源:https://stackoverflow.com/questions/60301694/action-reducer-mapping-has-type-error-with-multiple-action-types