Reputation: 2986
Let's say I have some code below:
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'.
The error indicate that call
is using its second overload signature, which I thought it would use its first overload in this specific case.
My understanding is that request
should match Requests
first overload, and then call(request, {})
matches Callable
first overload, but actually it does't. Where do I get it wrong?
So my question is Why call(request, {})
does not match <T>(fn: Func1<T>, arg1: T): string
?
Upvotes: 0
Views: 52
Reputation: 327744
The issue doesn't have to do with Callable
's overloads. You can see this for yourself by commenting out the second overload:
interface Callable {
<T>(fn: Func1<T>, arg1: T): string
//<T1>(fn: FuncArray<T1>, arg1: T1): number
}
Then you see the error:
const h = call(request, {}) // error!
// -------------------> ~~
// Argument of type '{}' is not assignable to parameter of type 'string'.
So the compiler has looked at request
and inferred that T
is type string
, and then {}
does not match string
and you get an error. So the issue is that the compiler does not accept call(request, {})
as a match for Callable
's first overload.
If you uncomment Callable
's second overload, the compiler sees that it doesn't match the second overload either, and the error changes to "no overload matches this call". So let's not worry about FuncArray
.
So why does call(request, {})
not match the first Callable
overload?
The problem is that Requests
is an overloaded function interface, and generic type parameter inference cannot do overload resolution at the same time. It's a design limitation of TypeScript. When the compiler sees call(request, {})
it has to infer the type of T
. Instead of trying to determine which of the two overloads of Requests
it should try to match against, it just picks the last one. And (url: string, config?: Config)=> string
matches Func1<string>
. And everything goes wrong from there.
So what can you do? The easiest thing is to manually specify the generic parameter to relieve the compiler of the burden of inferring it:
const h = call<Config>(request, {}) // okay
Once you specify that T
is Config
, the compiler then can do overload resolution on request
and verify that, yes, request
is a valid Func1<Config>
. Similarly you can widen the type of request
via type assertion to just Func1<Config>
, so that T
is correctly inferred:
const i = call(request as Func1<Config>, {}); // okay
Either way should work.
Okay, hope that helps; good luck!
Upvotes: 1