TypeScript 重载没有选择预期的重载
TypeScript overloads doesn't chose the expected one
假设我在下面有一些代码:
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
?
此问题与 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
的第二个重载,编译器会发现它也不匹配第二个重载,并且错误会更改为 "no overload matches this call"。所以我们不用担心 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)=> string
匹配 Func1<string>
。从那里一切都出错了。
那你能做什么?最简单的方法是手动指定泛型参数以减轻编译器推断它的负担:
const h = call<Config>(request, {}) // okay
一旦你指定 T
是 Config
,编译器 然后 可以对 request
进行重载解析并验证,是的, request
是有效的 Func1<Config>
。同样,您可以通过类型断言将 request
的类型扩展为 Func1<Config>
,以便正确推断出 T
:
const i = call(request as Func1<Config>, {}); // okay
任何一种方法都应该有效。
好的,希望对您有所帮助;祝你好运!
假设我在下面有一些代码:
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
?
此问题与 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
的第二个重载,编译器会发现它也不匹配第二个重载,并且错误会更改为 "no overload matches this call"。所以我们不用担心 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)=> string
匹配 Func1<string>
。从那里一切都出错了。
那你能做什么?最简单的方法是手动指定泛型参数以减轻编译器推断它的负担:
const h = call<Config>(request, {}) // okay
一旦你指定 T
是 Config
,编译器 然后 可以对 request
进行重载解析并验证,是的, request
是有效的 Func1<Config>
。同样,您可以通过类型断言将 request
的类型扩展为 Func1<Config>
,以便正确推断出 T
:
const i = call(request as Func1<Config>, {}); // okay
任何一种方法都应该有效。
好的,希望对您有所帮助;祝你好运!