Skip to content

Commit

Permalink
Ignore method bivariance in the subtype and strict subtype relationships
Browse files Browse the repository at this point in the history
Fix #41977
  • Loading branch information
RyanCavanaugh committed Dec 15, 2020
1 parent 3037443 commit 376a03b
Show file tree
Hide file tree
Showing 27 changed files with 372 additions and 95 deletions.
11 changes: 8 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ namespace ts {
StrictCallback = 1 << 1,
IgnoreReturnTypes = 1 << 2,
StrictArity = 1 << 3,
IgnoreBivariance = 1 << 4,
Callback = BivariantCallback | StrictCallback,
}

Expand Down Expand Up @@ -16280,8 +16281,12 @@ namespace ts {
}

const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
const strictVariance = (checkMode & SignatureCheckMode.IgnoreBivariance) ||
(!(checkMode & SignatureCheckMode.Callback) &&
strictFunctionTypes &&
kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature &&
kind !== SyntaxKind.Constructor);
let result = Ternary.True;

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

function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ var cs = [a, b, c]; // { x: number; y?: number };[]
>c : { x: number; a?: number; }

var ds = [(x: Object) => 1, (x: string) => 2]; // { (x:Object) => number }[]
>ds : ((x: Object) => number)[]
>[(x: Object) => 1, (x: string) => 2] : ((x: Object) => number)[]
>ds : ((x: string) => number)[]
>[(x: Object) => 1, (x: string) => 2] : ((x: string) => number)[]
>(x: Object) => 1 : (x: Object) => number
>x : Object
>1 : 1
Expand Down
14 changes: 7 additions & 7 deletions tests/baselines/reference/arrayOfFunctionTypes3.types
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,28 @@ var c: { (x: number): number; (x: any): any; };
>x : any

var z = [a, b, c];
>z : { (x: number): number; (x: any): any; }[]
>[a, b, c] : { (x: number): number; (x: any): any; }[]
>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
>[a, b, c] : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
>a : { (x: number): number; (x: string): string; }
>b : { (x: number): number; (x: string): string; }
>c : { (x: number): number; (x: any): any; }

var r4 = z[0];
>r4 : { (x: number): number; (x: any): any; }
>z[0] : { (x: number): number; (x: any): any; }
>z : { (x: number): number; (x: any): any; }[]
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
>z[0] : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
>0 : 0

var r5 = r4(''); // any not string
>r5 : any
>r4('') : any
>r4 : { (x: number): number; (x: any): any; }
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
>'' : ""

var r5b = r4(1);
>r5b : number
>r4(1) : number
>r4 : { (x: number): number; (x: any): any; }
>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
>1 : 1

var a2: { <T>(x: T): number; (x: string): string;};
Expand Down
18 changes: 6 additions & 12 deletions tests/baselines/reference/bestChoiceType.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// Repro from #10041

(''.match(/ /) || []).map(s => s.toLowerCase());
>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>''.match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
>match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))

// Similar cases

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

let z = y.map(s => s.toLowerCase());
>z : Symbol(z, Decl(bestChoiceType.ts, 9, 7))
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>y : Symbol(y, Decl(bestChoiceType.ts, 8, 7))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
}

function f2() {
Expand All @@ -51,12 +47,10 @@ function f2() {

let z = y.map(s => s.toLowerCase());
>z : Symbol(z, Decl(bestChoiceType.ts, 15, 7))
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>y : Symbol(y, Decl(bestChoiceType.ts, 14, 7))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
}

74 changes: 37 additions & 37 deletions tests/baselines/reference/bestChoiceType.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
// Repro from #10041

(''.match(/ /) || []).map(s => s.toLowerCase());
>(''.match(/ /) || []).map(s => s.toLowerCase()) : string[]
>(''.match(/ /) || []).map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>(''.match(/ /) || []) : RegExpMatchArray
>''.match(/ /) || [] : RegExpMatchArray
>(''.match(/ /) || []).map(s => s.toLowerCase()) : any[]
>(''.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[])
>(''.match(/ /) || []) : RegExpMatchArray | never[]
>''.match(/ /) || [] : RegExpMatchArray | never[]
>''.match(/ /) : RegExpMatchArray | null
>''.match : (regexp: string | RegExp) => RegExpMatchArray | null
>'' : ""
>match : (regexp: string | RegExp) => RegExpMatchArray | null
>/ / : RegExp
>[] : never[]
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>s => s.toLowerCase() : (s: string) => string
>s : string
>s.toLowerCase() : string
>s.toLowerCase : () => string
>s : string
>toLowerCase : () => string
>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[])
>s => s.toLowerCase() : (s: any) => any
>s : any
>s.toLowerCase() : any
>s.toLowerCase : any
>s : any
>toLowerCase : any

// Similar cases

Expand All @@ -34,23 +34,23 @@ function f1() {
>/ / : RegExp

let y = x || [];
>y : RegExpMatchArray
>x || [] : RegExpMatchArray
>y : RegExpMatchArray | never[]
>x || [] : RegExpMatchArray | never[]
>x : RegExpMatchArray | null
>[] : never[]

let z = y.map(s => s.toLowerCase());
>z : string[]
>y.map(s => s.toLowerCase()) : string[]
>y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>y : RegExpMatchArray
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>s => s.toLowerCase() : (s: string) => string
>s : string
>s.toLowerCase() : string
>s.toLowerCase : () => string
>s : string
>toLowerCase : () => string
>z : any[]
>y.map(s => s.toLowerCase()) : any[]
>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[])
>y : RegExpMatchArray | never[]
>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[])
>s => s.toLowerCase() : (s: any) => any
>s : any
>s.toLowerCase() : any
>s.toLowerCase : any
>s : any
>toLowerCase : any
}

function f2() {
Expand All @@ -65,23 +65,23 @@ function f2() {
>/ / : RegExp

let y = x ? x : [];
>y : RegExpMatchArray
>x ? x : [] : RegExpMatchArray
>y : RegExpMatchArray | never[]
>x ? x : [] : RegExpMatchArray | never[]
>x : RegExpMatchArray | null
>x : RegExpMatchArray
>[] : never[]

let z = y.map(s => s.toLowerCase());
>z : string[]
>y.map(s => s.toLowerCase()) : string[]
>y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>y : RegExpMatchArray
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>s => s.toLowerCase() : (s: string) => string
>s : string
>s.toLowerCase() : string
>s.toLowerCase : () => string
>s : string
>toLowerCase : () => string
>z : any[]
>y.map(s => s.toLowerCase()) : any[]
>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[])
>y : RegExpMatchArray | never[]
>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[])
>s => s.toLowerCase() : (s: any) => any
>s : any
>s.toLowerCase() : any
>s.toLowerCase : any
>s : any
>toLowerCase : any
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class C extends A {
}

var xs = [(x: A) => { }, (x: B) => { }, (x: C) => { }];
>xs : ((x: A) => void)[]
>[(x: A) => { }, (x: B) => { }, (x: C) => { }] : ((x: A) => void)[]
>xs : (((x: B) => void) | ((x: C) => void))[]
>[(x: A) => { }, (x: B) => { }, (x: C) => { }] : (((x: B) => void) | ((x: C) => void))[]
>(x: A) => { } : (x: A) => void
>x : A
>(x: B) => { } : (x: B) => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const [, a = ''] = ''.match('') || [];
> : undefined
>a : string
>'' : ""
>''.match('') || [] : RegExpMatchArray
>''.match('') || [] : RegExpMatchArray | undefined[]
>''.match('') : RegExpMatchArray
>''.match : (regexp: string | RegExp) => RegExpMatchArray
>'' : ""
Expand Down
23 changes: 23 additions & 0 deletions tests/baselines/reference/subtypeReduceBivariance1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/subtypeReduceBivariance1.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
Types of parameters 'a' and 'a' are incompatible.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.


==== tests/cases/compiler/subtypeReduceBivariance1.ts (1 errors) ====
interface S {
f(a: number | string): void;
}
declare const S: S;
function g(a: number): void { }

// Force type resolution
g;
S.f;

const arr: Array<(a: number | string) => void> = [g, S.f];
~
!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
!!! error TS2322: Types of parameters 'a' and 'a' are incompatible.
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
19 changes: 19 additions & 0 deletions tests/baselines/reference/subtypeReduceBivariance1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [subtypeReduceBivariance1.ts]
interface S {
f(a: number | string): void;
}
declare const S: S;
function g(a: number): void { }

// Force type resolution
g;
S.f;

const arr: Array<(a: number | string) => void> = [g, S.f];

//// [subtypeReduceBivariance1.js]
function g(a) { }
// Force type resolution
g;
S.f;
var arr = [g, S.f];
34 changes: 34 additions & 0 deletions tests/baselines/reference/subtypeReduceBivariance1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/subtypeReduceBivariance1.ts ===
interface S {
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))

f(a: number | string): void;
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 1, 6))
}
declare const S: S;
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))

function g(a: number): void { }
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 4, 11))

// Force type resolution
g;
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))

S.f;
>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))

const arr: Array<(a: number | string) => void> = [g, S.f];
>arr : Symbol(arr, Decl(subtypeReduceBivariance1.ts, 10, 5))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 10, 18))
>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19))
>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))
>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13))
>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13))

31 changes: 31 additions & 0 deletions tests/baselines/reference/subtypeReduceBivariance1.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=== tests/cases/compiler/subtypeReduceBivariance1.ts ===
interface S {
f(a: number | string): void;
>f : (a: number | string) => void
>a : string | number
}
declare const S: S;
>S : S

function g(a: number): void { }
>g : (a: number) => void
>a : number

// Force type resolution
g;
>g : (a: number) => void

S.f;
>S.f : (a: string | number) => void
>S : S
>f : (a: string | number) => void

const arr: Array<(a: number | string) => void> = [g, S.f];
>arr : ((a: number | string) => void)[]
>a : string | number
>[g, S.f] : ((a: number) => void)[]
>g : (a: number) => void
>S.f : (a: string | number) => void
>S : S
>f : (a: string | number) => void

23 changes: 23 additions & 0 deletions tests/baselines/reference/subtypeReduceBivariance2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/subtypeReduceBivariance2.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
Types of parameters 'a' and 'a' are incompatible.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.


==== tests/cases/compiler/subtypeReduceBivariance2.ts (1 errors) ====
interface S {
f(a: number | string): void;
}
declare const S: S;
function g(a: number): void { }

// Force type resolution
S.f;
g;

const arr: Array<(a: number | string) => void> = [g, S.f];
~
!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'.
!!! error TS2322: Types of parameters 'a' and 'a' are incompatible.
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
Loading

0 comments on commit 376a03b

Please sign in to comment.