【问题标题】:Typescript Generic Generic打字稿通用通用
【发布时间】:2021-02-14 10:18:13
【问题描述】:

不确定我追求的是什么打字稿功能,但我认为我需要一个通用的泛型(如果那是打字稿功能)。以下是我的要求。

我想表示一组事件/事件处理程序元组。以下是我目前所拥有的。

interface Event {
 type: string;
}

type EventType<T extends Event> = T['type'];
type EventHandler<T extends Event> = (event:T): void;

// so far so good
type EventTypeAndHandlerTuple <T extends Event> = [EventType<T>, EventHandler<T>];


// this is where I wish I could do better. I am forced to used any
type EventTypeAndHandlerTuples = EventTypeAndHandlerTuple<any>[]

我希望能够表示这样的代码

const todoAddedEvent: TodoAddedEvent = {...}
const handleTodoAddedEvent: TodoAddedHandler = (event: TodoAddedEvent) => {...};
const todoRemovedEvent: TodoRemovedEvent = {...}
const handleTodoRemovedEvent: TodoRemovedHandler = (event: TodoRemovedEvent) => {...}

// HELP ME GIVE THIS THING A TYPE ????
const eventTypeToHandlerTuples = [
 [todoAddedEvent, handleTodoAddedEvent],
 [todoRemovedEvent, handleTodoRemovedEvent]
];

我想要一个eventTypeToHandlerTuples 的类型,如果它是关联事件类型和事件处理程序元组的集合,它就会通过。如果事件类型与不正确的处理程序相关联,我希望此类型表示失败。例如,我希望这失败

// this should fail because the removed event is being paired with the add event handler.
const eventTypeToHandlerTuples = [
 [todoRemovedEvent, handleTodoAddedEvent], 
];

在此先感谢您的意见

【问题讨论】:

    标签: javascript typescript generics


    【解决方案1】:

    执行此操作的简单方法是收集您关心的所有Event 类型的联合;这可能是可能的,因为您显然希望他们的type 属性是唯一的string literal types,可以用于discriminate such a union。如果是这样,您定义联合:

    type Events = TodoAddedEvent | TodoRemovedEvent;
    

    然后使用它来描述 Events 联合中每个元素 T 的所有可能的 EventTypeAndHandlerTuple&lt;T&gt; 类型的联合,使用一些联合构建技术,例如 distributive conditional types

    type UnionOfEventTypeAndHandlerTuples = Events extends infer T ?
      T extends Event ? EventTypeAndHandlerTuple<T> : never : never;
    // type UnionOfEventTypeAndHandlerTuples = 
    // EventTypeAndHandlerTuple<TodoAddedEvent> | EventTypeAndHandlerTuple<TodoRemovedEvent>
    

    一旦你有了这个,你想要的类型就是UnionOfEventTypeAndHandlerTuples[]

    // okay
    const eventTypeToHandlerTuples: UnionOfEventTypeAndHandlerTuples[] = [
      [todoAddedEvent, handleTodoAddedEvent],
      [todoRemovedEvent, handleTodoRemovedEvent]
    ];
    
    // error
    const badEventTypeToHandlerTuples: UnionOfEventTypeAndHandlerTuples[] = [
      [todoRemovedEvent, handleTodoAddedEvent], // error!
    ];
    

    如果您没有预定义的联合,那么事情就会变得更加困难。如果没有像 TypeScript 中对 存在的泛型类型 的原生支持(请参阅 microsoft/TypeScript#14466),就没有像 EventTypeAndHandlerTuple&lt;exists T extends Event&gt;[] 这样的特定类型,其中 exists T 的意思是“我不知道或不在乎 T是,我只关心它存在”。您可以将存在类型视为所有可能匹配类型的“无限联合”。

    相反,您需要做其他事情。对于 TypeScript 来说,最合理的可能是在 TypeScript 中使用常规的旧泛型类型(这些被称为 universal 而不是 existential 并且表现得像“无限交集”而不是“无限联合” "),并使用辅助函数使编译器 infer 对你来说是通用的,这样你就不必把它写出来了:

    const asEventTypeToHandlerTuples = <T extends Event[]>(
      tuples: [...{ [I in keyof T]: EventTypeAndHandlerTuple<Extract<T[I], Event>> }]
    ) => tuples;
    

    辅助函数asEventTypeToHandlerTuples() 接受一个名为tuples 的参数并返回它。此参数的类型被限制为mapped tuple type,它将Event 类型的数组转换为EventTypeAndHandlerTuple 类型的相应数组。如果T的类型被推断为[TodoAddedEvent, TodoRemovedEvent, SomeOtherEvent],那么tuples()的类型被限制为[EventTypeAndHandlerTuple&lt;TodoAddedEvent&gt;, EventTypeAndHandlerTuple&lt;TodoRemovedEvent&gt;, EventTypeAndHandlerTuple&lt;SomeOtherEvent&gt;]。让我们看看它是如何工作的。我们不再注释变量的类型,而是让编译器推断它们:

    // okay
    const eventTypeToHandlerTuples = asEventTypeToHandlerTuples([
      [todoAddedEvent, handleTodoAddedEvent],
      [todoRemovedEvent, handleTodoRemovedEvent]
    ]);
    
    // error
    const badEventTypeToHandlerTuples = asEventTypeToHandlerTuples([
      [todoRemovedEvent, handleTodoAddedEvent], // error!
    ]);
    

    eventTypeToHandlerTuples 赋值没有问题,但badEventTypeToHandlerTuples 给出错误,因为它推断其类型为[EventTypeAndHandlerTuple&lt;TodoAddedEvent&gt;],但todoRemovedEvent 不能赋值给EventType&lt;TodoAddedEvent&gt;,你会得到所需的错误。


    我个人会尝试使用类似Events 的联合,因为处理特定类型的联合比将泛型类型参数拖到代码库中来表示约束要容易得多。但如果这不可行,通用辅助函数通常是您正在寻找的那种不可表示的存在类型的可接受替代品。

    Playground link to code

    【讨论】:

    • 非常感谢您的回答。它确实有帮助。如果我正在创建一个预期元组作为参数的函数,您对应该做什么有意见吗?
    • 我不太明白这个问题......这与这里的代码有关还是一个单独的问题? minimal reproducible example 代码示例会很有帮助
    • 我正在尝试在一个接收元组的 npm 模块中创建一个实用函数。作为一个库,我不一定知道用户将提供的 [event, eventHandler] 元组。如果元组没有通过类型检查,我希望能够提供消费开发人员的反馈。
    • 然后给实用函数一个通用签名,就像上面的 asEventTypeToHandlerTuples() 函数一样。
    猜你喜欢
    • 1970-01-01
    • 2017-04-15
    • 2019-04-18
    • 2020-07-29
    • 2019-06-28
    • 1970-01-01
    • 2020-05-13
    • 2015-10-07
    • 1970-01-01
    相关资源
    最近更新 更多