Redux减速器工厂类型问题中的Typescript映射类型

Posted

技术标签:

【中文标题】Redux减速器工厂类型问题中的Typescript映射类型【英文标题】:Typescirpt mapped typed in redux reducer factory typings problem 【发布时间】:2019-06-06 20:49:33 【问题描述】:

我正在尝试实现类型安全的功能,以创建基于“动作处理程序”映射的减速器。我们的想法是让 API 看起来像这样:

export const Actions = 
    setToken: (token: string) => createAction(SET_TOKEN_TYPE, token),
    invalidateToken: () => createAction(INVALIDATE_TOKEN_TYPE),
    startLogin: () => createAction(START_LOGIN_TYPE)
;

export const reducer = createReducer<State, typeof Actions>(
    
        [SET_TOKEN_TYPE]: ( loginError, ...state , action) => (
            ...state,
            token: action.payload,
            loading: false
        ),
        [INVALIDATE_TOKEN_TYPE]: ( token, ...state ) => state,
        [START_LOGIN_TYPE]: ( loginError, ...state ) => (
            ...state,
            loading: true
        )
    ,
    
        loading: false
    
);

createReducer 函数应该(为了清楚起见没有 Typescript)看起来像这样:

function createReducer(handlers, initialState) 
    return (state = initialState, action) => 
        if (action.type in handlers) 
            return handlers[action.type](state, action);
        
        return state;
    ;

我创建了这样的类型化函数来保证类型安全:

interface Action<T extends string> 
    type: T;

type ActionCreator<T extends string> = (...args: any) => Action<T>;
type ActionsCreators = 
    [creator: string]: ActionCreator<any>;
;
type ActionsUnion<Actions extends ActionsCreators> = ReturnType<
    Actions[keyof Actions]
>;
type ActionHandlers<ActionCreators extends ActionsCreators, State> = 
    [K in ReturnType<ActionCreators[keyof ActionCreators]>["type"]]: (
        state: State,
        action: ReturnType<ActionCreators[K]> 
    ) => State
;

    function createReducer<State, Actions extends ActionsCreators>(
    handlers: ActionHandlers<Actions, State>,
    initialState: State
) 
    return (
        state: State = initialState,
        action: ActionsUnion<Actions>
    ): State => 
        if (action.type in handlers) 
            // unfortunately action.type is here any :(
            return handlers[action.type](state, action); // here I have the error
        
        return state;
    ;

handlers[action.type] 我有错误(noImplicitAny: true

元素隐含地具有“any”类型,因为类型“ActionHandlers”没有索引签名。

知道如何在 reducer 中输入 action.type 吗?

你可以在the gist找到整个例子

【问题讨论】:

【参考方案1】:

您收到错误的原因是 action.type 被隐式键入为 any,因为没有应用适用的类型。

在链中的某个点,您使用 any 作为类型参数,用于需要植根于 string 的内容:

type ActionsCreators = 
    [creator: string]: ActionCreator<any>;
;

如果在这里添加类型参数可以替换any;但是,您需要一直传递下去。

请参阅以下已进行此更新的版本。我不得不将一些中间类型重命名为通用名称(TP),因为我很难保持正确的类型。

有了额外的类型参数&lt;P&gt;,我们现在有了一个用于以下而不是隐式any的类型:

const f = handlers[action.type];

这里,f 变为 ActionHandlers&lt;P, T, State&gt;[P]

export interface Action<T extends string> 
    type: T;


export interface ActionWithPayload<T extends string, P> extends Action<T> 
    payload: P;


export type ActionCreator<T extends string> = (...args: any) => Action<T>;

export type ActionsCreators<T extends string> = 
    [creator: string]: ActionCreator<T>;
;

export type ActionsUnion<P extends string, T extends ActionsCreators<P>> = ReturnType<T[keyof T]>;

export type ActionHandlers<P extends string, T extends ActionsCreators<P>, State> = 
    [K in ReturnType<T[keyof T]>["type"]]: (
        state: State,
        action: ReturnType<T[K]>
    ) => State
;

export function createReducer<P extends string, State, T extends ActionsCreators<P>>(
    handlers: ActionHandlers<P, T, State>,
    initialState: State
) 
    return (state: State = initialState, action: ActionsUnion<P, T>): State => 

        if (action.type in handlers) 
            const f = handlers[action.type];
            return f(state, action);
        

        return state;
    ;

【讨论】:

以上是关于Redux减速器工厂类型问题中的Typescript映射类型的主要内容,如果未能解决你的问题,请参考以下文章

使用 Redux 如何阻止 reducer 中的 switch 增长?

Redux 我可以在单独的减速器中使用一种动作类型吗?

Redux 动作工厂不适用于参数

如何访问减速器(redux-router)中的当前位置?

Redux - reducer 与动作的关系

Springboot + mybatis + React+redux+React-router+antd+Typescript: React+Typescrip项目的搭建