Typescript 无法键入每个键都映射到泛型值的对象

Typescript 无法键入每个键都映射到泛型值的对象,typescript,Typescript,我在键入一个对象时遇到一些问题,其中每个键都映射到一个通用值 我已将我的代码简化为以下最小可复制示例: export type Action<T extends string, P> = { type: T; error: false; payload: P; }; type Event = { action: string; [trait: string]: string; }; type Handlers = { [key: string]:

我在键入一个对象时遇到一些问题,其中每个键都映射到一个通用值

我已将我的代码简化为以下最小可复制示例:

export type Action<T extends string, P> = {
    type: T;
    error: false;
    payload: P;
};

type Event = { action: string; [trait: string]: string; };

type Handlers = {
    [key: string]: <T extends string, P>(action: Action<T, P>) => Event;
};

export type ActionAType = Action<
    "ACTION_A",
    { id: number; a: string; }
>;


export type ActionBType = Action<
    "ACTION_B",
    { id: number; b: string }
>;

const HANDLERS: Handlers = {
    "QUERY_GROUP_START": (action: ActionAType) => {
        return { action: "START", a: action.payload.a };
    },

    "QUERY_GROUP_CONTINUE": (action: ActionBType) => {
        return { action: "CONTINUE", id: action.payload.id };
    }
};
我之所以需要这种模式,是因为我在Redux中间件中有一组操作处理程序

中间件
功能的工作方式如下:

  • 它接收一个动作(类型为
    动作
  • 调用该操作的处理程序
  • 此中间件位于库中,因此没有处理程序列表。相反,当配置中间件时,库的客户端传入一个处理程序列表

    export const coreAnalytics = (
        customHandlers: Handlers
    ) => () => (next: (action: Action) => void) => (action: Action) => {
        const customHandler: (action: Action) => Event =
            customHandlers[action.type];
    
        if (customHandler) {
            const analyticsEvent: Event = customHandler(action);
    
            ...
        }
    
        ...
    };
    
    请注意,此处没有用于
    操作的联合类型。如果我们有这个功能,那么将其重写为一个打开操作类型并处理每个操作的函数将非常简单

    我这里有一个游乐场链接:
    这样想:在类型为
    处理程序的对象中,调用函数属性之一时,唯一的保证是其
    操作
    参数将是
    操作
    T
    可以是任何
    string
    P
    可以是任何东西。这使得在将属性分配给
    处理程序
    对象时要求任何更具体的保证都是错误的。在这两种情况下,您要求
    action
    具体为
    ActionAType
    ActionBType
    ,这意味着您要求
    T
    具体为
    'action\u A'
    'action\u B'
    ,而
    P
    具体为
    {id:number;A:string;}
    {id:number;b:string}
    。这对于如何键入
    处理程序来说太具体了

    我同意Federkun的观点,即最好的方法是不显式地键入
    处理程序
    (事实上,您可能根本不需要
    处理程序
    ,甚至不需要作为别名)。如果没有显式类型,对象上的每个属性都有自己的较窄类型,并有关于
    T
    p
    的相关保证


    如果您的完整代码有某些原因需要在
    处理程序上显式键入,那么您可能需要更新/扩展您的示例以反映这一点。

    常量处理程序:处理程序
    会释放指定值的类型信息,因此 在我看来,您可以只键入一个处理程序:
    (action:action)


    为什么这里需要
    处理程序
    作为一种类型?它会有什么用途?为什么不能是
    类型处理程序=类型处理程序
    ?@Federkun好问题,我已经尽可能地更新了这个问题来解释这一点。Redux委员会通常不再将操作作为联合类型键入。Redux maintainer@phry有一个article关于这一点:我将在稍后更详细地了解您的问题,但肯定有一些方法可以避免TS问题,而不是直接解决它。这是由于的概念。您是否尝试过使用接口而不是键入。如果您的操作是``导出接口法案`的接口,它会使事情变得更简单离子{type:string;error:boolean;payload:any;};``这是非常有见地的,您是对的,它需要更多的上下文,所以我编辑了我的问题,以便有更多的上下文来解释我为什么需要它。
    
    export const coreAnalytics = (
        customHandlers: Handlers
    ) => () => (next: (action: Action) => void) => (action: Action) => {
        const customHandler: (action: Action) => Event =
            customHandlers[action.type];
    
        if (customHandler) {
            const analyticsEvent: Event = customHandler(action);
    
            ...
        }
    
        ...
    };
    
    const asHandlers = <HS extends Handlers>(hs: HS) => hs
    
    const HANDLERS_TYPED = asHandlers({
        "QUERY_GROUP_START": (action: ActionAType) => {
            return true;
        },
    
        "QUERY_GROUP_CONTINUE": (action: ActionBType) => {
            return false;
        }
    })