TypeScript Version: 3.3.0-dev.20181205
Search Terms:
conditional type, infer, overload, no-arg, zero arguments, strictFunctionTypes
Note that this issue is not about the behavior where the last overload is examined in conditional type inference of a single signature, where some might expect a union or an argument-based choice.
Code
// turn on --strictFunctionTypes
type InferTwoOverloads<F extends Function> =
F extends { (...a1: infer A1): infer R1, (...a0: infer A0): infer R0 } ?
[(...a1: A1) => R1, (...a0: A0) => R0] :
never;
type Expected = InferTwoOverloads<((x: string) => number) & (() => string)>
// [(x: string) => number, () => string]
type JustOneSignature = InferTwoOverloads<((x: string) => number)>;
// never
type JustTheOtherSignature = InferTwoOverloads<(() => string)>;
// [(...a1: unknown[]) => {}, () => string]
Expected behavior:
I would expect that either both JustOneSignature and JustTheOtherSignature would be never, or both JustOneSignature and JustTheOtherSignature would evaluate to a two-tuple where the first signature is... something. I suppose (...a1: unknown[])=>{}? Or maybe it should be a copy of one of the other overloads? Or something in between?
Actual behavior:
It looks a zero-argument function is being treated pathologically during conditional type inference; if a type is callable with zero arguments, it will be considered assignable to any number of overloads with what looks like failed inference for the types of the arguments (unknown[]) and the return ({}). Otherwise, if a type is only callable with at least one argument, it will only be considered assignable to the matching number of overloads.
This distinction only seems to happen with --strictFunctionTypes on. If you turn it off, JustOneSignature above becomes [(...a1: unknown[]) => {}, (x: string) => number], which is at least consistent with the zero-argument situation.
Playground Link:
馃敆
Related Issues:
As i mentioned in duplicate issue - problem not only in no arg function. also with (x: unknown) => any, (x: any) => any, (...args: unknown[]) => any etc.
type Overloads<F> =
F extends {
(...args: infer A1): infer R1
(...args: infer A2): infer R2;
} ? {rule: 2, variants: [A1, R1] | [A2, R2]} :
F extends {
(...args: infer A1): infer R1;
} ? {rule: 1, variants: [A1, R1]} :
never;
declare const ok1: Overloads<(x: number) => boolean>;
// {rule: 1, variants: [[number], boolean]}
declare const ok2: Overloads<{(): 1; (x: number): 2}>;
// {rule: 2, variants: [[], 1] | [[number], 2]}
declare const wrong1: Overloads<() => boolean>;
// {rule: 2, variants: [[], boolean] | [unknown[], {}]}
// expected {rule: 1, variants: [[], boolean]}
declare const wrong2: Overloads<(...args: unknown[]) => boolean>;
// {rule: 2, variants: [unknown[], boolean] | [unknown[], {}]}
// expected {rule: 1, variants: [unknown[], boolean]}
declare const wrong3: Overloads<(x: unknown) => boolean>;
// {rule: 2, variants: [[unknown], boolean] | [unknown[], {}]}
// expected {rule: 1, variants: [[unknown], boolean]}
declare const wrong4: Overloads<(x: any) => boolean>;
// {rule: 2, variants: [[any], boolean] | [unknown[], {}]}
// expected {rule: 1, variants: [[any], boolean]}
Relevant SO question: How to use Parameters type on overloaded functions?
Most helpful comment
As i mentioned in duplicate issue - problem not only in no arg function. also with
(x: unknown) => any,(x: any) => any,(...args: unknown[]) => anyetc.