Skip to content

Commit 76c6a53

Browse files
ahejlsbergRyanCavanaughtypescript-bot
authored andcommitted
Improve inference between types with multiple signatures (microsoft#54448)
Co-authored-by: Ryan Cavanaugh <[email protected]> Co-authored-by: TypeScript Bot <[email protected]>
1 parent 8d8a2f1 commit 76c6a53

File tree

4 files changed

+413
-5
lines changed

4 files changed

+413
-5
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25535,12 +25535,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2553525535

2553625536
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
2553725537
const sourceSignatures = getSignaturesOfType(source, kind);
25538-
const targetSignatures = getSignaturesOfType(target, kind);
2553925538
const sourceLen = sourceSignatures.length;
25540-
const targetLen = targetSignatures.length;
25541-
const len = sourceLen < targetLen ? sourceLen : targetLen;
25542-
for (let i = 0; i < len; i++) {
25543-
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
25539+
if (sourceLen > 0) {
25540+
// We match source and target signatures from the bottom up, and if the source has fewer signatures
25541+
// than the target, we infer from the first source signature to the excess target signatures.
25542+
const targetSignatures = getSignaturesOfType(target, kind);
25543+
const targetLen = targetSignatures.length;
25544+
for (let i = 0; i < targetLen; i++) {
25545+
const sourceIndex = Math.max(sourceLen - targetLen + i, 0);
25546+
inferFromSignature(getBaseSignature(sourceSignatures[sourceIndex]), getErasedSignature(targetSignatures[i]));
25547+
}
2554425548
}
2554525549
}
2554625550

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//// [tests/cases/compiler/multiSignatureTypeInference.ts] ////
2+
3+
=== multiSignatureTypeInference.ts ===
4+
declare function f1(arg: boolean): string;
5+
>f1 : Symbol(f1, Decl(multiSignatureTypeInference.ts, 0, 0), Decl(multiSignatureTypeInference.ts, 0, 42), Decl(multiSignatureTypeInference.ts, 1, 56))
6+
>arg : Symbol(arg, Decl(multiSignatureTypeInference.ts, 0, 20))
7+
8+
declare function f1(arg1: number, arg2: number): number;
9+
>f1 : Symbol(f1, Decl(multiSignatureTypeInference.ts, 0, 0), Decl(multiSignatureTypeInference.ts, 0, 42), Decl(multiSignatureTypeInference.ts, 1, 56))
10+
>arg1 : Symbol(arg1, Decl(multiSignatureTypeInference.ts, 1, 20))
11+
>arg2 : Symbol(arg2, Decl(multiSignatureTypeInference.ts, 1, 33))
12+
13+
declare function f1(...args: string[]): string[];
14+
>f1 : Symbol(f1, Decl(multiSignatureTypeInference.ts, 0, 0), Decl(multiSignatureTypeInference.ts, 0, 42), Decl(multiSignatureTypeInference.ts, 1, 56))
15+
>args : Symbol(args, Decl(multiSignatureTypeInference.ts, 2, 20))
16+
17+
declare function f2(arg: unknown): unknown;
18+
>f2 : Symbol(f2, Decl(multiSignatureTypeInference.ts, 2, 49))
19+
>arg : Symbol(arg, Decl(multiSignatureTypeInference.ts, 4, 20))
20+
21+
declare function f3(): string;
22+
>f3 : Symbol(f3, Decl(multiSignatureTypeInference.ts, 4, 43))
23+
24+
type AllParams<T> =
25+
>AllParams : Symbol(AllParams, Decl(multiSignatureTypeInference.ts, 6, 30))
26+
>T : Symbol(T, Decl(multiSignatureTypeInference.ts, 8, 15))
27+
28+
T extends { (...a: infer A1): any, (...a: infer A2): any, (...a: infer A3): any, (...a: infer A4): any } ? A1 | A2 | A3 | A4 : never;
29+
>T : Symbol(T, Decl(multiSignatureTypeInference.ts, 8, 15))
30+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 9, 17))
31+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 9, 28))
32+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 9, 40))
33+
>A2 : Symbol(A2, Decl(multiSignatureTypeInference.ts, 9, 51))
34+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 9, 63))
35+
>A3 : Symbol(A3, Decl(multiSignatureTypeInference.ts, 9, 74))
36+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 9, 86))
37+
>A4 : Symbol(A4, Decl(multiSignatureTypeInference.ts, 9, 97))
38+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 9, 28))
39+
>A2 : Symbol(A2, Decl(multiSignatureTypeInference.ts, 9, 51))
40+
>A3 : Symbol(A3, Decl(multiSignatureTypeInference.ts, 9, 74))
41+
>A4 : Symbol(A4, Decl(multiSignatureTypeInference.ts, 9, 97))
42+
43+
type AllReturns<T> =
44+
>AllReturns : Symbol(AllReturns, Decl(multiSignatureTypeInference.ts, 9, 137))
45+
>T : Symbol(T, Decl(multiSignatureTypeInference.ts, 11, 16))
46+
47+
T extends { (...a: any[]): infer R1, (...a: any[]): infer R2, (...a: any[]): infer R3, (...a: any[]): infer R4 } ? R1 | R2 | R3 | R4 : never;
48+
>T : Symbol(T, Decl(multiSignatureTypeInference.ts, 11, 16))
49+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 12, 17))
50+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 12, 36))
51+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 12, 42))
52+
>R2 : Symbol(R2, Decl(multiSignatureTypeInference.ts, 12, 61))
53+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 12, 67))
54+
>R3 : Symbol(R3, Decl(multiSignatureTypeInference.ts, 12, 86))
55+
>a : Symbol(a, Decl(multiSignatureTypeInference.ts, 12, 92))
56+
>R4 : Symbol(R4, Decl(multiSignatureTypeInference.ts, 12, 111))
57+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 12, 36))
58+
>R2 : Symbol(R2, Decl(multiSignatureTypeInference.ts, 12, 61))
59+
>R3 : Symbol(R3, Decl(multiSignatureTypeInference.ts, 12, 86))
60+
>R4 : Symbol(R4, Decl(multiSignatureTypeInference.ts, 12, 111))
61+
62+
type Params1 = AllParams<typeof f1>; // string[] | [arg: boolean] | [arg1: number, arg2: number]
63+
>Params1 : Symbol(Params1, Decl(multiSignatureTypeInference.ts, 12, 145))
64+
>AllParams : Symbol(AllParams, Decl(multiSignatureTypeInference.ts, 6, 30))
65+
>f1 : Symbol(f1, Decl(multiSignatureTypeInference.ts, 0, 0), Decl(multiSignatureTypeInference.ts, 0, 42), Decl(multiSignatureTypeInference.ts, 1, 56))
66+
67+
type Params2 = AllParams<typeof f2>; // [arg: unknown]
68+
>Params2 : Symbol(Params2, Decl(multiSignatureTypeInference.ts, 14, 36))
69+
>AllParams : Symbol(AllParams, Decl(multiSignatureTypeInference.ts, 6, 30))
70+
>f2 : Symbol(f2, Decl(multiSignatureTypeInference.ts, 2, 49))
71+
72+
type Params3 = AllParams<typeof f3>; // []
73+
>Params3 : Symbol(Params3, Decl(multiSignatureTypeInference.ts, 15, 36))
74+
>AllParams : Symbol(AllParams, Decl(multiSignatureTypeInference.ts, 6, 30))
75+
>f3 : Symbol(f3, Decl(multiSignatureTypeInference.ts, 4, 43))
76+
77+
type Returns1 = AllReturns<typeof f1> // string | number | string[]
78+
>Returns1 : Symbol(Returns1, Decl(multiSignatureTypeInference.ts, 16, 36))
79+
>AllReturns : Symbol(AllReturns, Decl(multiSignatureTypeInference.ts, 9, 137))
80+
>f1 : Symbol(f1, Decl(multiSignatureTypeInference.ts, 0, 0), Decl(multiSignatureTypeInference.ts, 0, 42), Decl(multiSignatureTypeInference.ts, 1, 56))
81+
82+
type Returns2 = AllReturns<typeof f2>; // unknown
83+
>Returns2 : Symbol(Returns2, Decl(multiSignatureTypeInference.ts, 18, 37))
84+
>AllReturns : Symbol(AllReturns, Decl(multiSignatureTypeInference.ts, 9, 137))
85+
>f2 : Symbol(f2, Decl(multiSignatureTypeInference.ts, 2, 49))
86+
87+
type Returns3 = AllReturns<typeof f3>; // string
88+
>Returns3 : Symbol(Returns3, Decl(multiSignatureTypeInference.ts, 19, 38))
89+
>AllReturns : Symbol(AllReturns, Decl(multiSignatureTypeInference.ts, 9, 137))
90+
>f3 : Symbol(f3, Decl(multiSignatureTypeInference.ts, 4, 43))
91+
92+
// Repro from #28867
93+
94+
type InferTwoOverloads<F extends Function> =
95+
>InferTwoOverloads : Symbol(InferTwoOverloads, Decl(multiSignatureTypeInference.ts, 20, 38))
96+
>F : Symbol(F, Decl(multiSignatureTypeInference.ts, 24, 23))
97+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
98+
99+
F extends { (...a1: infer A1): infer R1, (...a0: infer A0): infer R0 } ?
100+
>F : Symbol(F, Decl(multiSignatureTypeInference.ts, 24, 23))
101+
>a1 : Symbol(a1, Decl(multiSignatureTypeInference.ts, 25, 15))
102+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 25, 27))
103+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 25, 38))
104+
>a0 : Symbol(a0, Decl(multiSignatureTypeInference.ts, 25, 44))
105+
>A0 : Symbol(A0, Decl(multiSignatureTypeInference.ts, 25, 56))
106+
>R0 : Symbol(R0, Decl(multiSignatureTypeInference.ts, 25, 67))
107+
108+
[(...a1: A1) => R1, (...a0: A0) => R0] :
109+
>a1 : Symbol(a1, Decl(multiSignatureTypeInference.ts, 26, 6))
110+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 25, 27))
111+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 25, 38))
112+
>a0 : Symbol(a0, Decl(multiSignatureTypeInference.ts, 26, 25))
113+
>A0 : Symbol(A0, Decl(multiSignatureTypeInference.ts, 25, 56))
114+
>R0 : Symbol(R0, Decl(multiSignatureTypeInference.ts, 25, 67))
115+
116+
never;
117+
118+
type Expected = InferTwoOverloads<((x: string) => number) & (() => string)>; // [(x: string) => number, () => string]
119+
>Expected : Symbol(Expected, Decl(multiSignatureTypeInference.ts, 27, 10))
120+
>InferTwoOverloads : Symbol(InferTwoOverloads, Decl(multiSignatureTypeInference.ts, 20, 38))
121+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 29, 36))
122+
123+
type JustOneSignature = InferTwoOverloads<((x: string) => number)>; // [(x: string) => number, (x: string) => number]
124+
>JustOneSignature : Symbol(JustOneSignature, Decl(multiSignatureTypeInference.ts, 29, 76))
125+
>InferTwoOverloads : Symbol(InferTwoOverloads, Decl(multiSignatureTypeInference.ts, 20, 38))
126+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 31, 44))
127+
128+
type JustTheOtherSignature = InferTwoOverloads<(() => string)>; // [() => string, () => string]
129+
>JustTheOtherSignature : Symbol(JustTheOtherSignature, Decl(multiSignatureTypeInference.ts, 31, 67))
130+
>InferTwoOverloads : Symbol(InferTwoOverloads, Decl(multiSignatureTypeInference.ts, 20, 38))
131+
132+
// Repro from #28867
133+
134+
type Overloads<F> =
135+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
136+
>F : Symbol(F, Decl(multiSignatureTypeInference.ts, 37, 15))
137+
138+
F extends {
139+
>F : Symbol(F, Decl(multiSignatureTypeInference.ts, 37, 15))
140+
141+
(...args: infer A1): infer R1
142+
>args : Symbol(args, Decl(multiSignatureTypeInference.ts, 39, 11))
143+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 39, 25))
144+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 39, 36))
145+
146+
(...args: infer A2): infer R2;
147+
>args : Symbol(args, Decl(multiSignatureTypeInference.ts, 40, 11))
148+
>A2 : Symbol(A2, Decl(multiSignatureTypeInference.ts, 40, 25))
149+
>R2 : Symbol(R2, Decl(multiSignatureTypeInference.ts, 40, 36))
150+
151+
} ? {rule: 2, variants: [A1, R1] | [A2, R2]} :
152+
>rule : Symbol(rule, Decl(multiSignatureTypeInference.ts, 41, 11))
153+
>variants : Symbol(variants, Decl(multiSignatureTypeInference.ts, 41, 19))
154+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 39, 25))
155+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 39, 36))
156+
>A2 : Symbol(A2, Decl(multiSignatureTypeInference.ts, 40, 25))
157+
>R2 : Symbol(R2, Decl(multiSignatureTypeInference.ts, 40, 36))
158+
159+
F extends {
160+
>F : Symbol(F, Decl(multiSignatureTypeInference.ts, 37, 15))
161+
162+
(...args: infer A1): infer R1;
163+
>args : Symbol(args, Decl(multiSignatureTypeInference.ts, 43, 11))
164+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 43, 25))
165+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 43, 36))
166+
167+
} ? {rule: 1, variants: [A1, R1]} :
168+
>rule : Symbol(rule, Decl(multiSignatureTypeInference.ts, 44, 11))
169+
>variants : Symbol(variants, Decl(multiSignatureTypeInference.ts, 44, 19))
170+
>A1 : Symbol(A1, Decl(multiSignatureTypeInference.ts, 43, 25))
171+
>R1 : Symbol(R1, Decl(multiSignatureTypeInference.ts, 43, 36))
172+
173+
never;
174+
175+
declare const ok1: Overloads<(x: number) => boolean>; // {rule: 2, variants: [[number], boolean]}
176+
>ok1 : Symbol(ok1, Decl(multiSignatureTypeInference.ts, 47, 13))
177+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
178+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 47, 30))
179+
180+
declare const ok2: Overloads<{(): 1; (x: number): 2}>; // {rule: 2, variants: [[], 1] | [[number], 2]}
181+
>ok2 : Symbol(ok2, Decl(multiSignatureTypeInference.ts, 49, 13))
182+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
183+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 49, 38))
184+
185+
declare const ok3: Overloads<() => boolean>; // {rule: 2, variants: [[], boolean] }
186+
>ok3 : Symbol(ok3, Decl(multiSignatureTypeInference.ts, 51, 13))
187+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
188+
189+
declare const ok4: Overloads<(...args: unknown[]) => boolean>; // {rule: 2, variants: [unknown[], boolean] }
190+
>ok4 : Symbol(ok4, Decl(multiSignatureTypeInference.ts, 53, 13))
191+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
192+
>args : Symbol(args, Decl(multiSignatureTypeInference.ts, 53, 30))
193+
194+
declare const ok5: Overloads<(x: unknown) => boolean>; // {rule: 2, variants: [[unknown], boolean] }
195+
>ok5 : Symbol(ok5, Decl(multiSignatureTypeInference.ts, 55, 13))
196+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
197+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 55, 30))
198+
199+
declare const ok6: Overloads<(x: any) => boolean>; // {rule: 2, variants: [[any], boolean] }
200+
>ok6 : Symbol(ok6, Decl(multiSignatureTypeInference.ts, 57, 13))
201+
>Overloads : Symbol(Overloads, Decl(multiSignatureTypeInference.ts, 33, 63))
202+
>x : Symbol(x, Decl(multiSignatureTypeInference.ts, 57, 30))
203+
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//// [tests/cases/compiler/multiSignatureTypeInference.ts] ////
2+
3+
=== multiSignatureTypeInference.ts ===
4+
declare function f1(arg: boolean): string;
5+
>f1 : { (arg: boolean): string; (arg1: number, arg2: number): number; (...args: string[]): string[]; }
6+
>arg : boolean
7+
8+
declare function f1(arg1: number, arg2: number): number;
9+
>f1 : { (arg: boolean): string; (arg1: number, arg2: number): number; (...args: string[]): string[]; }
10+
>arg1 : number
11+
>arg2 : number
12+
13+
declare function f1(...args: string[]): string[];
14+
>f1 : { (arg: boolean): string; (arg1: number, arg2: number): number; (...args: string[]): string[]; }
15+
>args : string[]
16+
17+
declare function f2(arg: unknown): unknown;
18+
>f2 : (arg: unknown) => unknown
19+
>arg : unknown
20+
21+
declare function f3(): string;
22+
>f3 : () => string
23+
24+
type AllParams<T> =
25+
>AllParams : AllParams<T>
26+
27+
T extends { (...a: infer A1): any, (...a: infer A2): any, (...a: infer A3): any, (...a: infer A4): any } ? A1 | A2 | A3 | A4 : never;
28+
>a : A1
29+
>a : A2
30+
>a : A3
31+
>a : A4
32+
33+
type AllReturns<T> =
34+
>AllReturns : AllReturns<T>
35+
36+
T extends { (...a: any[]): infer R1, (...a: any[]): infer R2, (...a: any[]): infer R3, (...a: any[]): infer R4 } ? R1 | R2 | R3 | R4 : never;
37+
>a : any[]
38+
>a : any[]
39+
>a : any[]
40+
>a : any[]
41+
42+
type Params1 = AllParams<typeof f1>; // string[] | [arg: boolean] | [arg1: number, arg2: number]
43+
>Params1 : string[] | [arg: boolean] | [arg1: number, arg2: number]
44+
>f1 : { (arg: boolean): string; (arg1: number, arg2: number): number; (...args: string[]): string[]; }
45+
46+
type Params2 = AllParams<typeof f2>; // [arg: unknown]
47+
>Params2 : [arg: unknown]
48+
>f2 : (arg: unknown) => unknown
49+
50+
type Params3 = AllParams<typeof f3>; // []
51+
>Params3 : []
52+
>f3 : () => string
53+
54+
type Returns1 = AllReturns<typeof f1> // string | number | string[]
55+
>Returns1 : string | number | string[]
56+
>f1 : { (arg: boolean): string; (arg1: number, arg2: number): number; (...args: string[]): string[]; }
57+
58+
type Returns2 = AllReturns<typeof f2>; // unknown
59+
>Returns2 : unknown
60+
>f2 : (arg: unknown) => unknown
61+
62+
type Returns3 = AllReturns<typeof f3>; // string
63+
>Returns3 : string
64+
>f3 : () => string
65+
66+
// Repro from #28867
67+
68+
type InferTwoOverloads<F extends Function> =
69+
>InferTwoOverloads : InferTwoOverloads<F>
70+
71+
F extends { (...a1: infer A1): infer R1, (...a0: infer A0): infer R0 } ?
72+
>a1 : A1
73+
>a0 : A0
74+
75+
[(...a1: A1) => R1, (...a0: A0) => R0] :
76+
>a1 : A1
77+
>a0 : A0
78+
79+
never;
80+
81+
type Expected = InferTwoOverloads<((x: string) => number) & (() => string)>; // [(x: string) => number, () => string]
82+
>Expected : [(x: string) => number, () => string]
83+
>x : string
84+
85+
type JustOneSignature = InferTwoOverloads<((x: string) => number)>; // [(x: string) => number, (x: string) => number]
86+
>JustOneSignature : [(x: string) => number, (x: string) => number]
87+
>x : string
88+
89+
type JustTheOtherSignature = InferTwoOverloads<(() => string)>; // [() => string, () => string]
90+
>JustTheOtherSignature : [() => string, () => string]
91+
92+
// Repro from #28867
93+
94+
type Overloads<F> =
95+
>Overloads : Overloads<F>
96+
97+
F extends {
98+
(...args: infer A1): infer R1
99+
>args : A1
100+
101+
(...args: infer A2): infer R2;
102+
>args : A2
103+
104+
} ? {rule: 2, variants: [A1, R1] | [A2, R2]} :
105+
>rule : 2
106+
>variants : [A1, R1] | [A2, R2]
107+
108+
F extends {
109+
(...args: infer A1): infer R1;
110+
>args : A1
111+
112+
} ? {rule: 1, variants: [A1, R1]} :
113+
>rule : 1
114+
>variants : [A1, R1]
115+
116+
never;
117+
118+
declare const ok1: Overloads<(x: number) => boolean>; // {rule: 2, variants: [[number], boolean]}
119+
>ok1 : { rule: 2; variants: [[x: number], boolean]; }
120+
>x : number
121+
122+
declare const ok2: Overloads<{(): 1; (x: number): 2}>; // {rule: 2, variants: [[], 1] | [[number], 2]}
123+
>ok2 : { rule: 2; variants: [[], 1] | [[x: number], 2]; }
124+
>x : number
125+
126+
declare const ok3: Overloads<() => boolean>; // {rule: 2, variants: [[], boolean] }
127+
>ok3 : { rule: 2; variants: [[], boolean]; }
128+
129+
declare const ok4: Overloads<(...args: unknown[]) => boolean>; // {rule: 2, variants: [unknown[], boolean] }
130+
>ok4 : { rule: 2; variants: [unknown[], boolean]; }
131+
>args : unknown[]
132+
133+
declare const ok5: Overloads<(x: unknown) => boolean>; // {rule: 2, variants: [[unknown], boolean] }
134+
>ok5 : { rule: 2; variants: [[x: unknown], boolean]; }
135+
>x : unknown
136+
137+
declare const ok6: Overloads<(x: any) => boolean>; // {rule: 2, variants: [[any], boolean] }
138+
>ok6 : { rule: 2; variants: [[x: any], boolean]; }
139+
>x : any
140+

0 commit comments

Comments
 (0)