Using Typescript types to ensure that all action types are handled by a reducer

ε祈祈猫儿з 提交于 2021-01-29 14:40:29

问题


I've attempted to create a kind of pattern-matching in typescript, particularly here for creating Redux reducers.

I'd really like to be able to specify ahead of time that a reducer must handle all actions of a particular type. I've tried to do that by creating an object which is keyed by action type, with values which are reducers. The typings for this mapping looks like:

export interface IReduxAction<T> {
    type: T;
}

interface IReducer<S, A> {
    (state: S, action: A): S;
}

export type IActionReducerMapping<S, A extends IReduxAction<string>> = Record<A['type'], IReducer<S, A>>;

This does most of what I was planning; any mapping created for a given action type must implement all of the types of action. The problem is that, with how I've set up the typings, the reducers in the mapping aren't able to infer the exact type of their action. For instance:

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> = {
    // here action is only aware of the properties on UserAction
    // ideally it'd know that it has access to the properties of
    // IAddUserAction
    '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,
        }
    }),
};

The problem is that the action in each reducer is only the union type UserAction, and so age, for instance, is not accessible. Is there a way to setup typings with this sort of pattern such that:

  1. I can ensure that all action types are handled, one way or another
  2. I can setup reducers which know the type of the action they are given

回答1:


You can solve the problem with the action not having the correct type by using a custom mapped type that maps over all the types (A['type']) and uses the Extract conditional type to get the correct action type for each type name.


export type IActionReducerMapping<S, A extends IReduxAction<string>> = {
    [K in A['type']]: IReducer<S, Extract<A, { type: K }>>
};

Playground Link



来源:https://stackoverflow.com/questions/60299124/using-typescript-types-to-ensure-that-all-action-types-are-handled-by-a-reducer

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