Skip to content

Commit 376a03b

Browse files
committed
Ignore method bivariance in the subtype and strict subtype relationships
Fix microsoft#41977
1 parent 3037443 commit 376a03b

27 files changed

+372
-95
lines changed

src/compiler/checker.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ namespace ts {
194194
StrictCallback = 1 << 1,
195195
IgnoreReturnTypes = 1 << 2,
196196
StrictArity = 1 << 3,
197+
IgnoreBivariance = 1 << 4,
197198
Callback = BivariantCallback | StrictCallback,
198199
}
199200

@@ -16280,8 +16281,12 @@ namespace ts {
1628016281
}
1628116282

1628216283
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
16283-
const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
16284-
kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
16284+
const strictVariance = (checkMode & SignatureCheckMode.IgnoreBivariance) ||
16285+
(!(checkMode & SignatureCheckMode.Callback) &&
16286+
strictFunctionTypes &&
16287+
kind !== SyntaxKind.MethodDeclaration &&
16288+
kind !== SyntaxKind.MethodSignature &&
16289+
kind !== SyntaxKind.Constructor);
1628516290
let result = Ternary.True;
1628616291

1628716292
const sourceThisType = getThisTypeOfSignature(source);
@@ -18538,7 +18543,7 @@ namespace ts {
1853818543
*/
1853918544
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
1854018545
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
18541-
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, makeFunctionTypeMapper(reportUnreliableMarkers));
18546+
relation === strictSubtypeRelation ? (SignatureCheckMode.StrictArity | SignatureCheckMode.IgnoreBivariance) : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, makeFunctionTypeMapper(reportUnreliableMarkers));
1854218547
}
1854318548

1854418549
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {

tests/baselines/reference/arrayLiteralWithMultipleBestCommonTypes.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ var cs = [a, b, c]; // { x: number; y?: number };[]
3636
>c : { x: number; a?: number; }
3737

3838
var ds = [(x: Object) => 1, (x: string) => 2]; // { (x:Object) => number }[]
39-
>ds : ((x: Object) => number)[]
40-
>[(x: Object) => 1, (x: string) => 2] : ((x: Object) => number)[]
39+
>ds : ((x: string) => number)[]
40+
>[(x: Object) => 1, (x: string) => 2] : ((x: string) => number)[]
4141
>(x: Object) => 1 : (x: Object) => number
4242
>x : Object
4343
>1 : 1

tests/baselines/reference/arrayOfFunctionTypes3.types

+7-7
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,28 @@ var c: { (x: number): number; (x: any): any; };
5050
>x : any
5151

5252
var z = [a, b, c];
53-
>z : { (x: number): number; (x: any): any; }[]
54-
>[a, b, c] : { (x: number): number; (x: any): any; }[]
53+
>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
54+
>[a, b, c] : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
5555
>a : { (x: number): number; (x: string): string; }
5656
>b : { (x: number): number; (x: string): string; }
5757
>c : { (x: number): number; (x: any): any; }
5858

5959
var r4 = z[0];
60-
>r4 : { (x: number): number; (x: any): any; }
61-
>z[0] : { (x: number): number; (x: any): any; }
62-
>z : { (x: number): number; (x: any): any; }[]
60+
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
61+
>z[0] : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
62+
>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
6363
>0 : 0
6464

6565
var r5 = r4(''); // any not string
6666
>r5 : any
6767
>r4('') : any
68-
>r4 : { (x: number): number; (x: any): any; }
68+
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
6969
>'' : ""
7070

7171
var r5b = r4(1);
7272
>r5b : number
7373
>r4(1) : number
74-
>r4 : { (x: number): number; (x: any): any; }
74+
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
7575
>1 : 1
7676

7777
var a2: { <T>(x: T): number; (x: string): string;};

tests/baselines/reference/bestChoiceType.symbols

+6-12
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22
// Repro from #10041
33

44
(''.match(/ /) || []).map(s => s.toLowerCase());
5-
>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
5+
>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
66
>''.match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
77
>match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
8-
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
8+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
99
>s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
10-
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
1110
>s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
12-
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
1311

1412
// Similar cases
1513

@@ -27,13 +25,11 @@ function f1() {
2725

2826
let z = y.map(s => s.toLowerCase());
2927
>z : Symbol(z, Decl(bestChoiceType.ts, 9, 7))
30-
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
28+
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
3129
>y : Symbol(y, Decl(bestChoiceType.ts, 8, 7))
32-
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
30+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
3331
>s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
34-
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
3532
>s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
36-
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
3733
}
3834

3935
function f2() {
@@ -51,12 +47,10 @@ function f2() {
5147

5248
let z = y.map(s => s.toLowerCase());
5349
>z : Symbol(z, Decl(bestChoiceType.ts, 15, 7))
54-
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
50+
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5551
>y : Symbol(y, Decl(bestChoiceType.ts, 14, 7))
56-
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
52+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5753
>s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
58-
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
5954
>s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
60-
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
6155
}
6256

tests/baselines/reference/bestChoiceType.types

+37-37
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22
// Repro from #10041
33

44
(''.match(/ /) || []).map(s => s.toLowerCase());
5-
>(''.match(/ /) || []).map(s => s.toLowerCase()) : string[]
6-
>(''.match(/ /) || []).map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
7-
>(''.match(/ /) || []) : RegExpMatchArray
8-
>''.match(/ /) || [] : RegExpMatchArray
5+
>(''.match(/ /) || []).map(s => s.toLowerCase()) : any[]
6+
>(''.match(/ /) || []).map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
7+
>(''.match(/ /) || []) : RegExpMatchArray | never[]
8+
>''.match(/ /) || [] : RegExpMatchArray | never[]
99
>''.match(/ /) : RegExpMatchArray | null
1010
>''.match : (regexp: string | RegExp) => RegExpMatchArray | null
1111
>'' : ""
1212
>match : (regexp: string | RegExp) => RegExpMatchArray | null
1313
>/ / : RegExp
1414
>[] : never[]
15-
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
16-
>s => s.toLowerCase() : (s: string) => string
17-
>s : string
18-
>s.toLowerCase() : string
19-
>s.toLowerCase : () => string
20-
>s : string
21-
>toLowerCase : () => string
15+
>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
16+
>s => s.toLowerCase() : (s: any) => any
17+
>s : any
18+
>s.toLowerCase() : any
19+
>s.toLowerCase : any
20+
>s : any
21+
>toLowerCase : any
2222

2323
// Similar cases
2424

@@ -34,23 +34,23 @@ function f1() {
3434
>/ / : RegExp
3535

3636
let y = x || [];
37-
>y : RegExpMatchArray
38-
>x || [] : RegExpMatchArray
37+
>y : RegExpMatchArray | never[]
38+
>x || [] : RegExpMatchArray | never[]
3939
>x : RegExpMatchArray | null
4040
>[] : never[]
4141

4242
let z = y.map(s => s.toLowerCase());
43-
>z : string[]
44-
>y.map(s => s.toLowerCase()) : string[]
45-
>y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
46-
>y : RegExpMatchArray
47-
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
48-
>s => s.toLowerCase() : (s: string) => string
49-
>s : string
50-
>s.toLowerCase() : string
51-
>s.toLowerCase : () => string
52-
>s : string
53-
>toLowerCase : () => string
43+
>z : any[]
44+
>y.map(s => s.toLowerCase()) : any[]
45+
>y.map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
46+
>y : RegExpMatchArray | never[]
47+
>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
48+
>s => s.toLowerCase() : (s: any) => any
49+
>s : any
50+
>s.toLowerCase() : any
51+
>s.toLowerCase : any
52+
>s : any
53+
>toLowerCase : any
5454
}
5555

5656
function f2() {
@@ -65,23 +65,23 @@ function f2() {
6565
>/ / : RegExp
6666

6767
let y = x ? x : [];
68-
>y : RegExpMatchArray
69-
>x ? x : [] : RegExpMatchArray
68+
>y : RegExpMatchArray | never[]
69+
>x ? x : [] : RegExpMatchArray | never[]
7070
>x : RegExpMatchArray | null
7171
>x : RegExpMatchArray
7272
>[] : never[]
7373

7474
let z = y.map(s => s.toLowerCase());
75-
>z : string[]
76-
>y.map(s => s.toLowerCase()) : string[]
77-
>y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
78-
>y : RegExpMatchArray
79-
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
80-
>s => s.toLowerCase() : (s: string) => string
81-
>s : string
82-
>s.toLowerCase() : string
83-
>s.toLowerCase : () => string
84-
>s : string
85-
>toLowerCase : () => string
75+
>z : any[]
76+
>y.map(s => s.toLowerCase()) : any[]
77+
>y.map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
78+
>y : RegExpMatchArray | never[]
79+
>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
80+
>s => s.toLowerCase() : (s: any) => any
81+
>s : any
82+
>s.toLowerCase() : any
83+
>s.toLowerCase : any
84+
>s : any
85+
>toLowerCase : any
8686
}
8787

tests/baselines/reference/contextualTypingArrayOfLambdas.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class C extends A {
2323
}
2424

2525
var xs = [(x: A) => { }, (x: B) => { }, (x: C) => { }];
26-
>xs : ((x: A) => void)[]
27-
>[(x: A) => { }, (x: B) => { }, (x: C) => { }] : ((x: A) => void)[]
26+
>xs : (((x: B) => void) | ((x: C) => void))[]
27+
>[(x: A) => { }, (x: B) => { }, (x: C) => { }] : (((x: B) => void) | ((x: C) => void))[]
2828
>(x: A) => { } : (x: A) => void
2929
>x : A
3030
>(x: B) => { } : (x: B) => void

tests/baselines/reference/initializedDestructuringAssignmentTypes.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const [, a = ''] = ''.match('') || [];
33
> : undefined
44
>a : string
55
>'' : ""
6-
>''.match('') || [] : RegExpMatchArray
6+
>''.match('') || [] : RegExpMatchArray | undefined[]
77
>''.match('') : RegExpMatchArray
88
>''.match : (regexp: string | RegExp) => RegExpMatchArray
99
>'' : ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/compiler/subtypeReduceBivariance1.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
2+
Types of parameters 'a' and 'a' are incompatible.
3+
Type 'string | number' is not assignable to type 'number'.
4+
Type 'string' is not assignable to type 'number'.
5+
6+
7+
==== tests/cases/compiler/subtypeReduceBivariance1.ts (1 errors) ====
8+
interface S {
9+
f(a: number | string): void;
10+
}
11+
declare const S: S;
12+
function g(a: number): void { }
13+
14+
// Force type resolution
15+
g;
16+
S.f;
17+
18+
const arr: Array<(a: number | string) => void> = [g, S.f];
19+
~
20+
!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
21+
!!! error TS2322: Types of parameters 'a' and 'a' are incompatible.
22+
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
23+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [subtypeReduceBivariance1.ts]
2+
interface S {
3+
f(a: number | string): void;
4+
}
5+
declare const S: S;
6+
function g(a: number): void { }
7+
8+
// Force type resolution
9+
g;
10+
S.f;
11+
12+
const arr: Array<(a: number | string) => void> = [g, S.f];
13+
14+
//// [subtypeReduceBivariance1.js]
15+
function g(a) { }
16+
// Force type resolution
17+
g;
18+
S.f;
19+
var arr = [g, S.f];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/subtypeReduceBivariance1.ts ===
2+
interface S {
3+
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
4+
5+
f(a: number | string): void;
6+
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
7+
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 1, 6))
8+
}
9+
declare const S: S;
10+
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
11+
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
12+
13+
function g(a: number): void { }
14+
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))
15+
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 4, 11))
16+
17+
// Force type resolution
18+
g;
19+
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))
20+
21+
S.f;
22+
>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
23+
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
24+
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
25+
26+
const arr: Array<(a: number | string) => void> = [g, S.f];
27+
>arr : Symbol(arr, Decl(subtypeReduceBivariance1.ts, 10, 5))
28+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
29+
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 10, 18))
30+
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))
31+
>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
32+
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
33+
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/subtypeReduceBivariance1.ts ===
2+
interface S {
3+
f(a: number | string): void;
4+
>f : (a: number | string) => void
5+
>a : string | number
6+
}
7+
declare const S: S;
8+
>S : S
9+
10+
function g(a: number): void { }
11+
>g : (a: number) => void
12+
>a : number
13+
14+
// Force type resolution
15+
g;
16+
>g : (a: number) => void
17+
18+
S.f;
19+
>S.f : (a: string | number) => void
20+
>S : S
21+
>f : (a: string | number) => void
22+
23+
const arr: Array<(a: number | string) => void> = [g, S.f];
24+
>arr : ((a: number | string) => void)[]
25+
>a : string | number
26+
>[g, S.f] : ((a: number) => void)[]
27+
>g : (a: number) => void
28+
>S.f : (a: string | number) => void
29+
>S : S
30+
>f : (a: string | number) => void
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/compiler/subtypeReduceBivariance2.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
2+
Types of parameters 'a' and 'a' are incompatible.
3+
Type 'string | number' is not assignable to type 'number'.
4+
Type 'string' is not assignable to type 'number'.
5+
6+
7+
==== tests/cases/compiler/subtypeReduceBivariance2.ts (1 errors) ====
8+
interface S {
9+
f(a: number | string): void;
10+
}
11+
declare const S: S;
12+
function g(a: number): void { }
13+
14+
// Force type resolution
15+
S.f;
16+
g;
17+
18+
const arr: Array<(a: number | string) => void> = [g, S.f];
19+
~
20+
!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
21+
!!! error TS2322: Types of parameters 'a' and 'a' are incompatible.
22+
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
23+
!!! error TS2322: Type 'string' is not assignable to type 'number'.

0 commit comments

Comments
 (0)