【问题标题】:Typescript infer function parameter unions打字稿推断函数参数联合
【发布时间】:2018-11-01 12:34:33
【问题描述】:

我目前有一个带有重载函数的接口,如下所示:

export interface IEvents {
  method(): boolean;
  on(name: 'eventName1', listener: (obj: SomeType) => void): void;
  on(name: 'eventName2', listener: (obj: SomeType) => void): void;
  on(name: 'eventName3', listener: (obj: SomeType) => void): void;
  on(name: 'eventName4', listener: (obj: SomeType) => void): void;
  on(name: 'eventName5', listener: (obj: SomeType) => void): void;
  on(name: 'eventName6', listener: () => void): void;
  on(name: 'eventName7', listener: (obj: SomeType) => void): void;
  on(name: 'eventName8', listener: (obj: SomeType) => void): void;
}

我正在尝试获取联合类型的事件名称,如下所示:

事件名称1 |事件名称2 | ...

我尝试了以下方法,但是当我推断类型时,它似乎只选择了一个名称值,而不是所有这些值的并集。

export type TEventExtension<T extends IEvents> {
  [K in keyof T]: K extends 'on' ? TEventListenerName<T[K]> : never;
}[keyof T];
export type TEventListenerName<T> = T extends (name: infer N, listener: (obj?: infer E) => void) => void ? N : never;
const ext: TEventExtension<IEvents> = void 0 as any; // Type: 'eventName8'

我也尝试过使用累加器类型来跟踪联合,但 Typescript 不允许递归泛型。

关于如何实现这一点的任何想法?

编辑:具有重载定义的接口存在于外部模块中。我试图避免 c+ping 从外部定义到我的定义,而是让它自动构建类型。

【问题讨论】:

  • on(name: "eventName1" | "eventName2", listener:() =&gt;void)
  • 不确定是否可以将on 的重载转换为联合类型(想不出办法,也许其他人有想法)。一种解决方法可能是这里介绍的方法stackoverflow.com/questions/50369299/…
  • @JonasW。这不是我想要完成的。我可以轻松地对接口中的事件名称进行硬编码,但我正在尝试创建一种类型,该类型采用模块定义的外部接口并返回可能的事件名称的联合。

标签: javascript typescript typescript-typings


【解决方案1】:

这不需要是一个单独的答案,但很难在评论中得到这个:重载特别不能以你想要的方式工作 type inference in conditional types:

当从具有多个调用签名的类型(例如重载函数的类型)进行推断时,会根据最后一个签名进行推断(这可能是最宽松的包罗万象的情况)。无法根据参数类型列表执行重载决策。

declare function foo(x: string): number;
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;
type T30 = ReturnType<typeof foo>;  // string | number

如果您不能将重载转换为具有参数联合的单个函数(使用条件类型来获取 listener 以使 "eventName6" 正确),那么我不知道以编程方式执行此操作的方法。

【讨论】:

  • 如果我理解正确,您提供的链接来自TypeScript 2.8 更改日志。这在TypeScript 4.4 中仍然不可能吗?
【解决方案2】:

我不认为你可以。在这个例子中使用类型推断:

interface SomeType {

}

export interface IEvents {
  method(): boolean;
  on(name: "eventName1", listener: (obj: SomeType) => void): 13;
  on(name: "eventName2", listener: (obj: SomeType) => void): void;
  on(name: "eventName3", listener: (obj: SomeType) => void): undefined;
  on(name: "eventName4", listener: (obj: SomeType) => void): 14;
  on(name: "eventName5", listener: (obj: SomeType) => void): 10;
  on(name: "eventName6", listener: () => void): "hola";
  on(name: "eventName8", listener: (obj: SomeType) => void): 200;
  on(name: "eventName7", listener: (obj: SomeType) => void): void;
}

const x: ReturnType<IEvents["on"]> = 200;

它责备 x 不是 void,但 IEvents 中可能的 ReturnType 之一是 200。看起来它只尝试最新的一个。

所以我猜如果你尝试对函数的第一个参数做类似的事情,它只会抓取最新的。

这里有 ReturnType 的定义:

https://github.com/Microsoft/TypeScript/blob/release-2.8/lib/lib.d.ts#L1386

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2021-07-22
    • 2021-01-03
    • 2020-03-22
    相关资源
    最近更新 更多