Merge pull request #26566 from Microsoft/fixStrictCoAndContraInferences

Properly handle co- and contra-variant inferences in strict mode
This commit is contained in:
Anders Hejlsberg 2018-08-21 16:11:52 -07:00 committed by GitHub
commit 7ec98afb8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 252 additions and 13 deletions

View file

@ -13638,10 +13638,11 @@ namespace ts {
if (!inferredType) {
const signature = context.signature;
if (signature) {
if (inference.contraCandidates) {
// If we have contravariant inferences we find the best common subtype and treat
// that as a single covariant candidate.
inference.candidates = append(inference.candidates, getContravariantInference(inference));
if (inference.contraCandidates && (!inference.candidates || inference.candidates.length === 1 && inference.candidates[0].flags & TypeFlags.Never)) {
// If we have contravariant inferences, but no covariant inferences or a single
// covariant inference of 'never', we find the best common subtype and treat that
// as a single covariant candidate.
inference.candidates = [getContravariantInference(inference)];
inference.contraCandidates = undefined;
}
if (inference.candidates) {

View file

@ -25,6 +25,29 @@ const x11 = f3(never, fo, fx); // "def"
declare function foo<T>(a: ReadonlyArray<T>): T;
let x = foo([]); // never
// Modified repros from #26127
interface A { a: string }
interface B extends A { b: string }
declare function acceptUnion(x: A | number): void;
declare function acceptA(x: A): void;
declare let a: A;
declare let b: B;
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
const t1: A = coAndContra(a, acceptUnion);
const t2: B = coAndContra(b, acceptA);
const t3: A = coAndContra(never, acceptA);
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
const t4: A[] = coAndContraArray([a], acceptUnion);
const t5: B[] = coAndContraArray([b], acceptA);
const t6: A[] = coAndContraArray([], acceptA);
//// [strictFunctionTypes1.js]
@ -36,6 +59,12 @@ var x4 = f4(fo, fs); // Func<string>
var x10 = f2(never, fo, fs); // string
var x11 = f3(never, fo, fx); // "def"
var x = foo([]); // never
var t1 = coAndContra(a, acceptUnion);
var t2 = coAndContra(b, acceptA);
var t3 = coAndContra(never, acceptA);
var t4 = coAndContraArray([a], acceptUnion);
var t5 = coAndContraArray([b], acceptA);
var t6 = coAndContraArray([], acceptA);
//// [strictFunctionTypes1.d.ts]
@ -50,11 +79,29 @@ declare function fo(x: Object): void;
declare function fs(x: string): void;
declare function fx(f: (x: "def") => void): void;
declare const x1: (x: string) => void;
declare const x2: string;
declare const x3: Object;
declare const x2 = "abc";
declare const x3: string;
declare const x4: Func<string>;
declare const never: never;
declare const x10: string;
declare const x11: Object;
declare const x11: "def";
declare function foo<T>(a: ReadonlyArray<T>): T;
declare let x: never;
interface A {
a: string;
}
interface B extends A {
b: string;
}
declare function acceptUnion(x: A | number): void;
declare function acceptA(x: A): void;
declare let a: A;
declare let b: B;
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
declare const t1: A;
declare const t2: B;
declare const t3: A;
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
declare const t4: A[];
declare const t5: B[];
declare const t6: A[];

View file

@ -125,3 +125,93 @@ let x = foo([]); // never
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 25, 3))
>foo : Symbol(foo, Decl(strictFunctionTypes1.ts, 20, 30))
// Modified repros from #26127
interface A { a: string }
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>a : Symbol(A.a, Decl(strictFunctionTypes1.ts, 29, 13))
interface B extends A { b: string }
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>b : Symbol(B.b, Decl(strictFunctionTypes1.ts, 30, 23))
declare function acceptUnion(x: A | number): void;
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 32, 29))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
declare function acceptA(x: A): void;
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 33, 25))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
declare let a: A;
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
declare let b: B;
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
>value : Symbol(value, Decl(strictFunctionTypes1.ts, 38, 32))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
>func : Symbol(func, Decl(strictFunctionTypes1.ts, 38, 41))
>t : Symbol(t, Decl(strictFunctionTypes1.ts, 38, 49))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 38, 29))
const t1: A = coAndContra(a, acceptUnion);
>t1 : Symbol(t1, Decl(strictFunctionTypes1.ts, 40, 5))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
const t2: B = coAndContra(b, acceptA);
>t2 : Symbol(t2, Decl(strictFunctionTypes1.ts, 41, 5))
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
const t3: A = coAndContra(never, acceptA);
>t3 : Symbol(t3, Decl(strictFunctionTypes1.ts, 42, 5))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>coAndContra : Symbol(coAndContra, Decl(strictFunctionTypes1.ts, 36, 17))
>never : Symbol(never, Decl(strictFunctionTypes1.ts, 17, 13))
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
>value : Symbol(value, Decl(strictFunctionTypes1.ts, 44, 37))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
>func : Symbol(func, Decl(strictFunctionTypes1.ts, 44, 48))
>t : Symbol(t, Decl(strictFunctionTypes1.ts, 44, 56))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 44, 34))
const t4: A[] = coAndContraArray([a], acceptUnion);
>t4 : Symbol(t4, Decl(strictFunctionTypes1.ts, 46, 5))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
>a : Symbol(a, Decl(strictFunctionTypes1.ts, 35, 11))
>acceptUnion : Symbol(acceptUnion, Decl(strictFunctionTypes1.ts, 30, 35))
const t5: B[] = coAndContraArray([b], acceptA);
>t5 : Symbol(t5, Decl(strictFunctionTypes1.ts, 47, 5))
>B : Symbol(B, Decl(strictFunctionTypes1.ts, 29, 25))
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
>b : Symbol(b, Decl(strictFunctionTypes1.ts, 36, 11))
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))
const t6: A[] = coAndContraArray([], acceptA);
>t6 : Symbol(t6, Decl(strictFunctionTypes1.ts, 48, 5))
>A : Symbol(A, Decl(strictFunctionTypes1.ts, 25, 16))
>coAndContraArray : Symbol(coAndContraArray, Decl(strictFunctionTypes1.ts, 42, 42))
>acceptA : Symbol(acceptA, Decl(strictFunctionTypes1.ts, 32, 50))

View file

@ -53,16 +53,16 @@ const x1 = f1(fo, fs); // (x: string) => void
>fs : (x: string) => void
const x2 = f2("abc", fo, fs); // "abc"
>x2 : string
>f2("abc", fo, fs) : string
>x2 : "abc"
>f2("abc", fo, fs) : "abc"
>f2 : <T>(obj: T, f1: (x: T) => void, f2: (x: T) => void) => T
>"abc" : "abc"
>fo : (x: Object) => void
>fs : (x: string) => void
const x3 = f3("abc", fo, fx); // "abc" | "def"
>x3 : Object
>f3("abc", fo, fx) : Object
>x3 : "def" | "abc"
>f3("abc", fo, fx) : "def" | "abc"
>f3 : <T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T
>"abc" : "abc"
>fo : (x: Object) => void
@ -87,8 +87,8 @@ const x10 = f2(never, fo, fs); // string
>fs : (x: string) => void
const x11 = f3(never, fo, fx); // "def"
>x11 : Object
>f3(never, fo, fx) : Object
>x11 : "def"
>f3(never, fo, fx) : "def"
>f3 : <T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T
>never : never
>fo : (x: Object) => void
@ -106,3 +106,81 @@ let x = foo([]); // never
>foo : <T>(a: ReadonlyArray<T>) => T
>[] : never[]
// Modified repros from #26127
interface A { a: string }
>a : string
interface B extends A { b: string }
>b : string
declare function acceptUnion(x: A | number): void;
>acceptUnion : (x: number | A) => void
>x : number | A
declare function acceptA(x: A): void;
>acceptA : (x: A) => void
>x : A
declare let a: A;
>a : A
declare let b: B;
>b : B
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
>coAndContra : <T>(value: T, func: (t: T) => void) => T
>value : T
>func : (t: T) => void
>t : T
const t1: A = coAndContra(a, acceptUnion);
>t1 : A
>coAndContra(a, acceptUnion) : A
>coAndContra : <T>(value: T, func: (t: T) => void) => T
>a : A
>acceptUnion : (x: number | A) => void
const t2: B = coAndContra(b, acceptA);
>t2 : B
>coAndContra(b, acceptA) : B
>coAndContra : <T>(value: T, func: (t: T) => void) => T
>b : B
>acceptA : (x: A) => void
const t3: A = coAndContra(never, acceptA);
>t3 : A
>coAndContra(never, acceptA) : A
>coAndContra : <T>(value: T, func: (t: T) => void) => T
>never : never
>acceptA : (x: A) => void
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
>value : T[]
>func : (t: T) => void
>t : T
const t4: A[] = coAndContraArray([a], acceptUnion);
>t4 : A[]
>coAndContraArray([a], acceptUnion) : A[]
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
>[a] : A[]
>a : A
>acceptUnion : (x: number | A) => void
const t5: B[] = coAndContraArray([b], acceptA);
>t5 : B[]
>coAndContraArray([b], acceptA) : B[]
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
>[b] : B[]
>b : B
>acceptA : (x: A) => void
const t6: A[] = coAndContraArray([], acceptA);
>t6 : A[]
>coAndContraArray([], acceptA) : A[]
>coAndContraArray : <T>(value: T[], func: (t: T) => void) => T[]
>[] : never[]
>acceptA : (x: A) => void

View file

@ -27,3 +27,26 @@ const x11 = f3(never, fo, fx); // "def"
declare function foo<T>(a: ReadonlyArray<T>): T;
let x = foo([]); // never
// Modified repros from #26127
interface A { a: string }
interface B extends A { b: string }
declare function acceptUnion(x: A | number): void;
declare function acceptA(x: A): void;
declare let a: A;
declare let b: B;
declare function coAndContra<T>(value: T, func: (t: T) => void): T;
const t1: A = coAndContra(a, acceptUnion);
const t2: B = coAndContra(b, acceptA);
const t3: A = coAndContra(never, acceptA);
declare function coAndContraArray<T>(value: T[], func: (t: T) => void): T[];
const t4: A[] = coAndContraArray([a], acceptUnion);
const t5: B[] = coAndContraArray([b], acceptA);
const t6: A[] = coAndContraArray([], acceptA);