TypeScript Version: 3.4.5
Search Terms: function with no parameter extends; function without parameter extends
Code
type T = (() => number) extends () => infer R ? R : never // T is number
type T = (() => number) extends (...args: []) => infer R ? R : never // T is number
type T = (() => number) extends (...args: [string]) => infer R ? R : never // T is **still** number
type T = (() => number) extends (...args: [string, boolean]) => infer R ? R : never // T is **still** number
type T = (() => number) extends (arg: string) => infer R ? R : never // T is **still** number
Expected behavior:
Function type with no parameter should not match function type with arguments in extends clause.
Actual behavior:
Function type with no parameter succeeds in all kinds of parameter matching in extends clause.
Related Issues: Not found
From the FAQ: a function with fewer parameters is intentionally assignable to (i.e., extends) a function that takes more parameters.
Thanks for the FAQ reference!
This mechanism makes me doubt how to differentiate the union of functions according to how a function gets called.
Assume there's a WebAPI of type { "/user": ((id: string, k: number) => User) | (() => User[]) }. For some reason, it is represented this way rather than function overloading. Then how can I utilize this union of function to reach the same effect of overloading?
That is, assume there's a function getUser: ApiFunc<"/user">, then the expectation is:
getUser(); // returns User[]
getUser("123456", 1); // returns User
One approach I tried is:
type ApiParams<Api> = Api extends (...args: infer P) => any ? P : never;
type ApiReturn<Api, Params extends any[]> = Api extends (...args: Params) => infer R ? R : never
type ApiFunc<Url extends keyof Apis> = <
Params extends ApiParams<Apis[Url]>,
>(url: Url, ...args: Params) => ApiReturn<Apis[Url], Params>;
Here, I rely on the params pattern matching to reduce the union type.
That is to say, in ApiReturn, if Params = [string, number], Api = Apis["/user"], then there is
((id: string, k: number) => User) | (() => User[]) extends (...args: [string, number]) => infer R ? R : never
<=>
((id: string, k: number) => User) extends (...args: [string, number]) => infer R ? R : never |
(() => User[]) extends (...args: [string, number]) => infer R ? R : never
<=>
User | never
<=>
User
However, since () => User[] is assignable to (...args: [string, number]) => infer R ? R : never, it will return User[] rather than never......
So, is there alternative to convert union of function type to the same effect of function overloading?
(Seems like this is a question that should turn to StackOverflow...)
Most helpful comment
From the FAQ: a function with fewer parameters is intentionally assignable to (i.e., extends) a function that takes more parameters.