Action-Reducer Mapping has type error with multiple action types

假装没事ソ 提交于 2020-03-05 02:18:54

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!