Typescript 类型中的嵌套三元条件扩展

Nested ternary condition inside Typescript type extends

我正在从 JavaScript 开发过渡到强制 TypeScript 键入的雇主。我为包含典型 .on(event, handler).

的模型创建了一个 Interface

我的同事提出了一些我认为可以理解的代码,但它使用了这种嵌套的三元语句,这让人难以阅读。

interface Model {
    ...
    on<T extends "added" | "failed" | "initialized" | "changed" | "removed"> (
        event: T,
        handler: T extends "changed" | "removed"
                 ? (T extends "changed"
                     ? (model: Model, entity: Entity, data: object) => void
                     : (model: Model, id: string) => void)
                 : (model: Model, entity: Entity) => void,
        context?: object
    ): void
}

这似乎是一种根据传入 event 的参数为 handler 参数设置不同签名的方法。

它似乎有效,但我想知道是否有不同的模式来编写它,这样它更容易阅读和理解。

我会考虑使用映射接口并对其进行索引,因为在 TypeScript 中,对象类型自然会为您提供操作 "map a string literal to a particular type"。虽然您 可以 使用条件类型来做到这一点,但它往往更难阅读和维护。

作为重构的第一步,我会做这样的事情:

// use a mapping from event name to event handler type
interface EventHandlerMap {
  added: (model: Model, entity: Entity) => void;
  failed: (model: Model, entity: Entity) => void;
  initialized: (model: Model, entity: Entity) => void;
  changed: (model: Model, entity: Entity, data: object) => void;
  removed: (model: Model, id: string) => void;
}

interface Model {
  on<T extends keyof EventHandlerMap>(
    event: T,
    handler: EventHandlerMap[T], // index into the mapping
    context?: object
  ): void;
}

您可以说服自己它的行为方式相同(或使用 link 到代码末尾查看)。 EventHandlerMap 接口只是一个助手,您实际上并未将其分配给任何对象。但它描述了从 Model.on()event 类型 T 参数到 handler 参数的映射。我们使用 lookup typeshandler 类型提取为 EventHandlerMap[T]

有可能进一步重构这个...例如,所有这些都是返回 void 并且第一个参数是 Model 的函数;也许您可以 EventHandlerMap 只跟踪不同事件之间的不同之处,然后使用更复杂的类型操作,但这可能比它的价值更麻烦。

好的,希望对您有所帮助;祝你好运!

Link to code