typescript - 根据事件键缩小事件回调的类型

标签 typescript

在搜索有关如何编写类型安全事件发射器的选项时,我偶然发现了这种模式,您首先在接口(interface)中定义事件名称及其值,例如:

interface UserEvents {
    nameChanged: string;
    phoneChanged: number;
}

并且“on”方法被定义为采用keyof UserEvents,以及一个回调,其参数是由使用称为查找类型的功能获取的 key - UserEvents[keyof T]:

function on<T extends keyof UserEvents>(e: T, cb: (val: UserEvents[T]) => void) {/**/}

提供类型安全的方法调用:

on('nameChanged', (val) => {}); // callback parameter type inferred to be a string
on('phoneChanged', (val) => {}); // ... inferred as number

我遇到的问题是在“on”方法的主体内部,我无法根据传递的键值缩小传递的回调的类型。由于事件名称及其类型是在同一接口(interface)中定义的,因此如果我检查 key 类型,我预计回调会缩小,但事实并非如此:

function on<T extends keyof UserEvents>(e: T, cb: (_: UserEvents[T]) => void) {
    if (e === 'nameChanged') {
        // expecting the cb type to be (string) => void
    } else {
        // and here to be (number) => void
    }

    // but regardless of the checks, the cb type
    // inside the method body is of type (string & number) => void
}

是否有任何方法可以使用其他 TypeScript 功能(例如类型保护、可区分联合等)以及此特定方法调用签名来实现基于事件键的回调的自动类型推断?

最佳答案

为了实现想要的行为,我们需要将两者组合成一个数据结构,幸运的是当选择 tuple 时对于这样的结构并将其与扩展函数参数相结合,结果将满足确切的需求。

考虑下面的代码:

interface UserEvents {
    nameChanged: string;
    phoneChanged: number;
}

// generic type in order to use with different objects then UserEvents only
type KeyWithCallback<A extends object> = {
  [K in keyof A]: [K, (_: A[K]) => void]
}[keyof A];

function on(...args: KeyWithCallback<UserEvents>) {
  if (args[0] === 'nameChanged') {
    const [_, clb] = args; // destructuring inside condition
    clb('') // here clb allows on string only (string) => void
  } else {
    const [_, clb] = args; // destructuring inside condition
    clb(1); // here clb allows on number only (number) => void
  }
}
// using
on('nameChanged', (a:string) => {}) // ok
on('nameChanged', (a:number) => {}) // error as expected

我用过mapped type KeyWithCallback将所有可能的参数表示为 [key, callback]对。结果是对的并集(第二个元组)。

KeyWithCallback<UserEvents>评估为:

| ["nameChanged", (_: string) => void] 
| ["phoneChanged", (_: number) => void]

第二件重要的事情是注释函数参数 ...args: KeyWithCallback<UserEvents> 。从现在开始,函数体将理解函数有两个参数,一个参数与另一个参数相关,并且这种关系在联合类型的每一对中进行描述。

检查该对的第一个元素会自动推断第二个元素。

关于typescript - 根据事件键缩小事件回调的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59174764/

相关文章:

javascript - 当 JavaScript 更改时,Angular 无法通过 [(ngModel)] 获取输入值

typescript :对象和基元之间的 keyof typeof union 永远不会

Angular 2 : Why do I need classes like Hero. ts?

javascript - 在 Node.js 中获取错误的日期

typescript - React Native/Typescript/Eslint 错误 : Type 'void' is not assignable to type 'CompositeAnimation'

javascript - ANTLR4 JavaScript Target非常慢

javascript - 可滚动的 highchart 在 JS 中工作,但在运行 Angular HighCharts 时遇到问题?

javascript - 如何在 Typescript 中为 getter 使用 Object.defineProperty?

javascript - Visual Studio 2015 Update 2 中的 TypeScript 模块 - 'require' 未定义

typescript - 在字典属性中推断通用类型