TypeScript重载未选择预期的重载

时间:2019-12-26 12:27:27

标签: typescript generics overloading

假设我下面有一些代码:


type Func1<T1> = (arg1: T1) => any;

type FuncArray<T1> = (arg1: T1, b: string) => any

interface Callable {
    <T>(fn: Func1<T>, arg1: T): string
    <T1>(fn: FuncArray<T1>, arg1: T1): number
}

const call: Callable = (...args: any): any => { }

interface Config {
    url?: string
    headers?: { [K: string]: string | number }
}

interface Requests {
    (config: Config): string
    (url: string, config?: Config): string
}

const request: Requests = (url: string | Config, config?: Config) => '123'

const h = call(request, {}) // error: Argument of type 'Requests' is not assignable to parameter of type 'FuncArray<string>'.
  // Types of parameters 'config' and 'arg1' are incompatible.
    // Type 'string' has no properties in common with type 'Config'.

该错误表明call正在使用其第二个过载签名,我认为在此特定情况下它将使用其第一个过载。

我的理解是request应该匹配Requests第一次重载,然后call(request, {})匹配Callable第一次重载,但实际上不匹配。我在哪里弄错了?

所以我的问题是为什么call(request, {})<T>(fn: Func1<T>, arg1: T): string不匹配?

1 个答案:

答案 0 :(得分:1)

此问题与Callable的重载无关。您可以通过注释掉第二个重载来自己查看:

interface Callable {
    <T>(fn: Func1<T>, arg1: T): string
    //<T1>(fn: FuncArray<T1>, arg1: T1): number
}

然后您看到错误:

const h = call(request, {}) // error!
// -------------------> ~~
// Argument of type '{}' is not assignable to parameter of type 'string'.

因此,编译器查看了request并推断出T的类型为string,然后{}string不匹配,您会得到一个错误。因此,问题在于编译器不接受call(request, {})作为Callable的第一个重载的匹配项。

如果取消注释Callable的第二次重载,编译器会发现它也不匹配第二次重载,并且错误更改为“此调用没有重载”。因此,不必担心FuncArray


那么call(request, {})为什么不匹配第一个Callable重载?

问题在于 Requests 是一个重载的函数接口,并且是generic type parameter inference cannot do overload resolution at the same time。这是TypeScript的设计限制。当编译器看到call(request, {})时,它必须推断T的类型。 它没有选择确定应该Requests的两个重载中的哪个重载,而是选择了最后一个。并且(url: string, config?: Config)=> stringFunc1<string>相匹配。一切都从那里出错。


那你该怎么办?最简单的方法是手动指定泛型参数,以减轻编译器进行推断的负担:

const h = call<Config>(request, {}) // okay

一旦指定TConfig,编译器 then 就可以对request进行重载解析,并验证是的,request是有效的Func1<Config>。同样,您可以通过类型断言将request的类型扩展为Func1<Config>,以便正确推断T

const i = call(request as Func1<Config>, {}); // okay

这两种方法都应该起作用。


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

Playground link to code