From 15eaba254b005f08831afa4004515c76812d64ca Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 20 Aug 2018 16:04:11 -0700 Subject: [PATCH 1/5] Covariant inferences (other than never) preferred over contravariant --- src/compiler/checker.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 26b93b19335b4..c9f70463db839 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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) { From a0df1e35e9f37b0cbf9eee28aa0df1bd3da06e2d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 20 Aug 2018 16:04:21 -0700 Subject: [PATCH 2/5] Accept new baselines --- tests/baselines/reference/strictFunctionTypes1.js | 6 +++--- tests/baselines/reference/strictFunctionTypes1.types | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/strictFunctionTypes1.js b/tests/baselines/reference/strictFunctionTypes1.js index 45d28f72501c5..94ee06f88f8fe 100644 --- a/tests/baselines/reference/strictFunctionTypes1.js +++ b/tests/baselines/reference/strictFunctionTypes1.js @@ -50,11 +50,11 @@ 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; declare const never: never; declare const x10: string; -declare const x11: Object; +declare const x11: "def"; declare function foo(a: ReadonlyArray): T; declare let x: never; diff --git a/tests/baselines/reference/strictFunctionTypes1.types b/tests/baselines/reference/strictFunctionTypes1.types index 6a824d8ff7ea9..0a1a952c0b57e 100644 --- a/tests/baselines/reference/strictFunctionTypes1.types +++ b/tests/baselines/reference/strictFunctionTypes1.types @@ -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 : (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 : (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 : (obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T >never : never >fo : (x: Object) => void From 88f7759d6baf4bcd456b722c63caa18ac799463e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 20 Aug 2018 16:21:25 -0700 Subject: [PATCH 3/5] Add tests --- tests/cases/compiler/strictFunctionTypes1.ts | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/cases/compiler/strictFunctionTypes1.ts b/tests/cases/compiler/strictFunctionTypes1.ts index ad8836fcf05b3..411aab812df73 100644 --- a/tests/cases/compiler/strictFunctionTypes1.ts +++ b/tests/cases/compiler/strictFunctionTypes1.ts @@ -27,3 +27,27 @@ const x11 = f3(never, fo, fx); // "def" declare function foo(a: ReadonlyArray): 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 let never: never; + +declare function coAndContra(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(value: T[], func: (t: T) => void): T[]; + +const t4: A[] = coAndContraArray([a], acceptUnion); +const t5: B[] = coAndContraArray([b], acceptA); +const t6: A[] = coAndContraArray([], acceptA); From 886a6d7473d3d13010dcaacad3c19916e467ed55 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 20 Aug 2018 16:23:09 -0700 Subject: [PATCH 4/5] Fix test --- tests/cases/compiler/strictFunctionTypes1.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/cases/compiler/strictFunctionTypes1.ts b/tests/cases/compiler/strictFunctionTypes1.ts index 411aab812df73..19dbe37cb79a2 100644 --- a/tests/cases/compiler/strictFunctionTypes1.ts +++ b/tests/cases/compiler/strictFunctionTypes1.ts @@ -38,7 +38,6 @@ declare function acceptA(x: A): void; declare let a: A; declare let b: B; -declare let never: never; declare function coAndContra(value: T, func: (t: T) => void): T; From f182389c5ba9405b1d8f7ecadd652967e38160f9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 20 Aug 2018 16:23:21 -0700 Subject: [PATCH 5/5] Accept new baselines --- .../reference/strictFunctionTypes1.js | 47 ++++++++++ .../reference/strictFunctionTypes1.symbols | 90 +++++++++++++++++++ .../reference/strictFunctionTypes1.types | 78 ++++++++++++++++ 3 files changed, 215 insertions(+) diff --git a/tests/baselines/reference/strictFunctionTypes1.js b/tests/baselines/reference/strictFunctionTypes1.js index 94ee06f88f8fe..c12e56545f6e3 100644 --- a/tests/baselines/reference/strictFunctionTypes1.js +++ b/tests/baselines/reference/strictFunctionTypes1.js @@ -25,6 +25,29 @@ const x11 = f3(never, fo, fx); // "def" declare function foo(a: ReadonlyArray): 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(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(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 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] @@ -58,3 +87,21 @@ declare const x10: string; declare const x11: "def"; declare function foo(a: ReadonlyArray): 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(value: T, func: (t: T) => void): T; +declare const t1: A; +declare const t2: B; +declare const t3: A; +declare function coAndContraArray(value: T[], func: (t: T) => void): T[]; +declare const t4: A[]; +declare const t5: B[]; +declare const t6: A[]; diff --git a/tests/baselines/reference/strictFunctionTypes1.symbols b/tests/baselines/reference/strictFunctionTypes1.symbols index ed657495cc467..911c561094b90 100644 --- a/tests/baselines/reference/strictFunctionTypes1.symbols +++ b/tests/baselines/reference/strictFunctionTypes1.symbols @@ -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(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(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)) + diff --git a/tests/baselines/reference/strictFunctionTypes1.types b/tests/baselines/reference/strictFunctionTypes1.types index 0a1a952c0b57e..058b066f4c5ae 100644 --- a/tests/baselines/reference/strictFunctionTypes1.types +++ b/tests/baselines/reference/strictFunctionTypes1.types @@ -106,3 +106,81 @@ let x = foo([]); // never >foo : (a: ReadonlyArray) => 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(value: T, func: (t: T) => void): T; +>coAndContra : (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 : (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 : (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 : (value: T, func: (t: T) => void) => T +>never : never +>acceptA : (x: A) => void + +declare function coAndContraArray(value: T[], func: (t: T) => void): T[]; +>coAndContraArray : (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 : (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 : (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 : (value: T[], func: (t: T) => void) => T[] +>[] : never[] +>acceptA : (x: A) => void +