Skip to content

Commit 7ec98af

Browse files
authored
Merge pull request #26566 from Microsoft/fixStrictCoAndContraInferences
Properly handle co- and contra-variant inferences in strict mode
2 parents 13bd478 + f182389 commit 7ec98af

File tree

5 files changed

+252
-13
lines changed

5 files changed

+252
-13
lines changed

Diff for: src/compiler/checker.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -13638,10 +13638,11 @@ namespace ts {
1363813638
if (!inferredType) {
1363913639
const signature = context.signature;
1364013640
if (signature) {
13641-
if (inference.contraCandidates) {
13642-
// If we have contravariant inferences we find the best common subtype and treat
13643-
// that as a single covariant candidate.
13644-
inference.candidates = append(inference.candidates, getContravariantInference(inference));
13641+
if (inference.contraCandidates && (!inference.candidates || inference.candidates.length === 1 && inference.candidates[0].flags & TypeFlags.Never)) {
13642+
// If we have contravariant inferences, but no covariant inferences or a single
13643+
// covariant inference of 'never', we find the best common subtype and treat that
13644+
// as a single covariant candidate.
13645+
inference.candidates = [getContravariantInference(inference)];
1364513646
inference.contraCandidates = undefined;
1364613647
}
1364713648
if (inference.candidates) {

Diff for: tests/baselines/reference/strictFunctionTypes1.js

+50-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,29 @@ const x11 = f3(never, fo, fx); // "def"
2525

2626
declare function foo<T>(a: ReadonlyArray<T>): T;
2727
let x = foo([]); // never
28+
29+
// Modified repros from #26127
30+
31+
interface A { a: string }
32+
interface B extends A { b: string }
33+
34+
declare function acceptUnion(x: A | number): void;
35+
declare function acceptA(x: A): void;
36+
37+
declare let a: A;
38+
declare let b: B;
39+
40+
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
41+
42+
const t1: A = coAndContra(a, acceptUnion);
43+
const t2: B = coAndContra(b, acceptA);
44+
const t3: A = coAndContra(never, acceptA);
45+
46+
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
47+
48+
const t4: A[] = coAndContraArray([a], acceptUnion);
49+
const t5: B[] = coAndContraArray([b], acceptA);
50+
const t6: A[] = coAndContraArray([], acceptA);
2851

2952

3053
//// [strictFunctionTypes1.js]
@@ -36,6 +59,12 @@ var x4 = f4(fo, fs); // Func<string>
3659
var x10 = f2(never, fo, fs); // string
3760
var x11 = f3(never, fo, fx); // "def"
3861
var x = foo([]); // never
62+
var t1 = coAndContra(a, acceptUnion);
63+
var t2 = coAndContra(b, acceptA);
64+
var t3 = coAndContra(never, acceptA);
65+
var t4 = coAndContraArray([a], acceptUnion);
66+
var t5 = coAndContraArray([b], acceptA);
67+
var t6 = coAndContraArray([], acceptA);
3968

4069

4170
//// [strictFunctionTypes1.d.ts]
@@ -50,11 +79,29 @@ declare function fo(x: Object): void;
5079
declare function fs(x: string): void;
5180
declare function fx(f: (x: "def") => void): void;
5281
declare const x1: (x: string) => void;
53-
declare const x2: string;
54-
declare const x3: Object;
82+
declare const x2 = "abc";
83+
declare const x3: string;
5584
declare const x4: Func<string>;
5685
declare const never: never;
5786
declare const x10: string;
58-
declare const x11: Object;
87+
declare const x11: "def";
5988
declare function foo<T>(a: ReadonlyArray<T>): T;
6089
declare let x: never;
90+
interface A {
91+
a: string;
92+
}
93+
interface B extends A {
94+
b: string;
95+
}
96+
declare function acceptUnion(x: A | number): void;
97+
declare function acceptA(x: A): void;
98+
declare let a: A;
99+
declare let b: B;
100+
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
101+
declare const t1: A;
102+
declare const t2: B;
103+
declare const t3: A;
104+
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
105+
declare const t4: A[];
106+
declare const t5: B[];
107+
declare const t6: A[];

Diff for: tests/baselines/reference/strictFunctionTypes1.symbols

+90
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,93 @@ let x = foo([]); // never
125125
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 25, 3))
126126
>foo : Symbol(foo, Decl(strictFunctionTypes1.ts, 20, 30))
127127

128+
// Modified repros from #26127
129+
130+
interface A { a: string }
131+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
132+
>a : Symbol(A.a, Decl(strictFunctionTypes1.ts, 29, 13))
133+
134+
interface B extends A { b: string }
135+
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
136+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
137+
>b : Symbol(B.b, Decl(strictFunctionTypes1.ts, 30, 23))
138+
139+
declare function acceptUnion(x: A | number): void;
140+
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
141+
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 32, 29))
142+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
143+
144+
declare function acceptA(x: A): void;
145+
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
146+
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 33, 25))
147+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
148+
149+
declare let a: A;
150+
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
151+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
152+
153+
declare let b: B;
154+
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
155+
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
156+
157+
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
158+
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
159+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
160+
>value : Symbol(value, Decl(strictFunctionTypes1.ts, 38, 32))
161+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
162+
>func : Symbol(func, Decl(strictFunctionTypes1.ts, 38, 41))
163+
>t : Symbol(t, Decl(strictFunctionTypes1.ts, 38, 49))
164+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
165+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
166+
167+
const t1: A = coAndContra(a, acceptUnion);
168+
>t1 : Symbol(t1, Decl(strictFunctionTypes1.ts, 40, 5))
169+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
170+
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
171+
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
172+
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
173+
174+
const t2: B = coAndContra(b, acceptA);
175+
>t2 : Symbol(t2, Decl(strictFunctionTypes1.ts, 41, 5))
176+
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
177+
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
178+
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
179+
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
180+
181+
const t3: A = coAndContra(never, acceptA);
182+
>t3 : Symbol(t3, Decl(strictFunctionTypes1.ts, 42, 5))
183+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
184+
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
185+
>never : Symbol(never, Decl(strictFunctionTypes1.ts, 17, 13))
186+
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
187+
188+
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
189+
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
190+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
191+
>value : Symbol(value, Decl(strictFunctionTypes1.ts, 44, 37))
192+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
193+
>func : Symbol(func, Decl(strictFunctionTypes1.ts, 44, 48))
194+
>t : Symbol(t, Decl(strictFunctionTypes1.ts, 44, 56))
195+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
196+
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
197+
198+
const t4: A[] = coAndContraArray([a], acceptUnion);
199+
>t4 : Symbol(t4, Decl(strictFunctionTypes1.ts, 46, 5))
200+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
201+
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
202+
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
203+
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
204+
205+
const t5: B[] = coAndContraArray([b], acceptA);
206+
>t5 : Symbol(t5, Decl(strictFunctionTypes1.ts, 47, 5))
207+
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
208+
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
209+
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
210+
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
211+
212+
const t6: A[] = coAndContraArray([], acceptA);
213+
>t6 : Symbol(t6, Decl(strictFunctionTypes1.ts, 48, 5))
214+
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
215+
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
216+
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
217+

Diff for: tests/baselines/reference/strictFunctionTypes1.types

+84-6
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@ const x1 = f1(fo, fs); // (x: string) => void
5353
>fs : (x: string) => void
5454

5555
const x2 = f2("abc", fo, fs); // "abc"
56-
>x2 : string
57-
>f2("abc", fo, fs) : string
56+
>x2 : "abc"
57+
>f2("abc", fo, fs) : "abc"
5858
>f2 : <T>(obj: T, f1: (x: T) => void, f2: (x: T) => void) => T
5959
>"abc" : "abc"
6060
>fo : (x: Object) => void
6161
>fs : (x: string) => void
6262

6363
const x3 = f3("abc", fo, fx); // "abc" | "def"
64-
>x3 : Object
65-
>f3("abc", fo, fx) : Object
64+
>x3 : "def" | "abc"
65+
>f3("abc", fo, fx) : "def" | "abc"
6666
>f3 : <T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T
6767
>"abc" : "abc"
6868
>fo : (x: Object) => void
@@ -87,8 +87,8 @@ const x10 = f2(never, fo, fs); // string
8787
>fs : (x: string) => void
8888

8989
const x11 = f3(never, fo, fx); // "def"
90-
>x11 : Object
91-
>f3(never, fo, fx) : Object
90+
>x11 : "def"
91+
>f3(never, fo, fx) : "def"
9292
>f3 : <T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T
9393
>never : never
9494
>fo : (x: Object) => void
@@ -106,3 +106,81 @@ let x = foo([]); // never
106106
>foo : <T>(a: ReadonlyArray<T>) => T
107107
>[] : never[]
108108

109+
// Modified repros from #26127
110+
111+
interface A { a: string }
112+
>a : string
113+
114+
interface B extends A { b: string }
115+
>b : string
116+
117+
declare function acceptUnion(x: A | number): void;
118+
>acceptUnion : (x: number | A) => void
119+
>x : number | A
120+
121+
declare function acceptA(x: A): void;
122+
>acceptA : (x: A) => void
123+
>x : A
124+
125+
declare let a: A;
126+
>a : A
127+
128+
declare let b: B;
129+
>b : B
130+
131+
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
132+
>coAndContra : <T>(value: T, func: (t: T) => void) => T
133+
>value : T
134+
>func : (t: T) => void
135+
>t : T
136+
137+
const t1: A = coAndContra(a, acceptUnion);
138+
>t1 : A
139+
>coAndContra(a, acceptUnion) : A
140+
>coAndContra : <T>(value: T, func: (t: T) => void) => T
141+
>a : A
142+
>acceptUnion : (x: number | A) => void
143+
144+
const t2: B = coAndContra(b, acceptA);
145+
>t2 : B
146+
>coAndContra(b, acceptA) : B
147+
>coAndContra : <T>(value: T, func: (t: T) => void) => T
148+
>b : B
149+
>acceptA : (x: A) => void
150+
151+
const t3: A = coAndContra(never, acceptA);
152+
>t3 : A
153+
>coAndContra(never, acceptA) : A
154+
>coAndContra : <T>(value: T, func: (t: T) => void) => T
155+
>never : never
156+
>acceptA : (x: A) => void
157+
158+
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
159+
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
160+
>value : T[]
161+
>func : (t: T) => void
162+
>t : T
163+
164+
const t4: A[] = coAndContraArray([a], acceptUnion);
165+
>t4 : A[]
166+
>coAndContraArray([a], acceptUnion) : A[]
167+
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
168+
>[a] : A[]
169+
>a : A
170+
>acceptUnion : (x: number | A) => void
171+
172+
const t5: B[] = coAndContraArray([b], acceptA);
173+
>t5 : B[]
174+
>coAndContraArray([b], acceptA) : B[]
175+
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
176+
>[b] : B[]
177+
>b : B
178+
>acceptA : (x: A) => void
179+
180+
const t6: A[] = coAndContraArray([], acceptA);
181+
>t6 : A[]
182+
>coAndContraArray([], acceptA) : A[]
183+
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
184+
>[] : never[]
185+
>acceptA : (x: A) => void
186+

Diff for: tests/cases/compiler/strictFunctionTypes1.ts

+23
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,26 @@ const x11 = f3(never, fo, fx); // "def"
2727

2828
declare function foo<T>(a: ReadonlyArray<T>): T;
2929
let x = foo([]); // never
30+
31+
// Modified repros from #26127
32+
33+
interface A { a: string }
34+
interface B extends A { b: string }
35+
36+
declare function acceptUnion(x: A | number): void;
37+
declare function acceptA(x: A): void;
38+
39+
declare let a: A;
40+
declare let b: B;
41+
42+
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
43+
44+
const t1: A = coAndContra(a, acceptUnion);
45+
const t2: B = coAndContra(b, acceptA);
46+
const t3: A = coAndContra(never, acceptA);
47+
48+
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
49+
50+
const t4: A[] = coAndContraArray([a], acceptUnion);
51+
const t5: B[] = coAndContraArray([b], acceptA);
52+
const t6: A[] = coAndContraArray([], acceptA);

0 commit comments

Comments
 (0)