From a287ddc93bda8043ca4c6b5f1cbf15f25c50791c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 6 Nov 2017 09:25:41 -0800 Subject: [PATCH 1/2] Fix invariant generic error elaboration logic --- src/compiler/checker.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e2e45b2ccb..4d5449a952 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9432,6 +9432,7 @@ namespace ts { function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { let result: Ternary; + let originalErrorInfo: DiagnosticMessageChain; const saveErrorInfo = errorInfo; if (target.flags & TypeFlags.TypeParameter) { // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P]. @@ -9511,6 +9512,7 @@ namespace ts { // if we have indexed access types with identical index types, see if relationship holds for // the two object types. if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { + errorInfo = saveErrorInfo; return result; } } @@ -9542,6 +9544,10 @@ namespace ts { if (!(reportErrors && some(variances, v => v === Variance.Invariant))) { return Ternary.False; } + // We remember the original error information so we can restore it in case the structural + // comparison unexpectedly succeeds. This can happen when the structural comparison result + // is a Ternary.Maybe for example caused by the recursion depth limiter. + originalErrorInfo = errorInfo; errorInfo = saveErrorInfo; } } @@ -9580,8 +9586,11 @@ namespace ts { } } if (result) { - errorInfo = saveErrorInfo; - return result; + if (!originalErrorInfo) { + errorInfo = saveErrorInfo; + return result; + } + errorInfo = originalErrorInfo; } } } From baafe5157eb273d1c87d71ff9e270e25e15d7b05 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 6 Nov 2017 09:25:51 -0800 Subject: [PATCH 2/2] Add regression test --- ...nvariantGenericErrorElaboration.errors.txt | 54 +++++++++++++ .../invariantGenericErrorElaboration.js | 30 +++++++ .../invariantGenericErrorElaboration.symbols | 76 ++++++++++++++++++ .../invariantGenericErrorElaboration.types | 78 +++++++++++++++++++ .../invariantGenericErrorElaboration.ts | 24 ++++++ 5 files changed, 262 insertions(+) create mode 100644 tests/baselines/reference/invariantGenericErrorElaboration.errors.txt create mode 100644 tests/baselines/reference/invariantGenericErrorElaboration.js create mode 100644 tests/baselines/reference/invariantGenericErrorElaboration.symbols create mode 100644 tests/baselines/reference/invariantGenericErrorElaboration.types create mode 100644 tests/cases/compiler/invariantGenericErrorElaboration.ts diff --git a/tests/baselines/reference/invariantGenericErrorElaboration.errors.txt b/tests/baselines/reference/invariantGenericErrorElaboration.errors.txt new file mode 100644 index 0000000000..5142b665d7 --- /dev/null +++ b/tests/baselines/reference/invariantGenericErrorElaboration.errors.txt @@ -0,0 +1,54 @@ +tests/cases/compiler/invariantGenericErrorElaboration.ts(3,7): error TS2322: Type 'Num' is not assignable to type 'Runtype'. + Types of property 'constraint' are incompatible. + Type 'Constraint' is not assignable to type 'Constraint>'. + Types of property 'constraint' are incompatible. + Type 'Constraint>' is not assignable to type 'Constraint>>'. + Types of property 'constraint' are incompatible. + Type 'Constraint>>' is not assignable to type 'Constraint>>>'. + Type 'Constraint>>' is not assignable to type 'Constraint>'. + Types of property 'underlying' are incompatible. + Type 'Constraint>' is not assignable to type 'Constraint'. +tests/cases/compiler/invariantGenericErrorElaboration.ts(4,17): error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype; }'. + Property 'foo' is incompatible with index signature. + Type 'Num' is not assignable to type 'Runtype'. + + +==== tests/cases/compiler/invariantGenericErrorElaboration.ts (2 errors) ==== + // Repro from #19746 + + const wat: Runtype = Num; + ~~~ +!!! error TS2322: Type 'Num' is not assignable to type 'Runtype'. +!!! error TS2322: Types of property 'constraint' are incompatible. +!!! error TS2322: Type 'Constraint' is not assignable to type 'Constraint>'. +!!! error TS2322: Types of property 'constraint' are incompatible. +!!! error TS2322: Type 'Constraint>' is not assignable to type 'Constraint>>'. +!!! error TS2322: Types of property 'constraint' are incompatible. +!!! error TS2322: Type 'Constraint>>' is not assignable to type 'Constraint>>>'. +!!! error TS2322: Type 'Constraint>>' is not assignable to type 'Constraint>'. +!!! error TS2322: Types of property 'underlying' are incompatible. +!!! error TS2322: Type 'Constraint>' is not assignable to type 'Constraint'. + const Foo = Obj({ foo: Num }) + ~~~~~~~~~~~~ +!!! error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype; }'. +!!! error TS2345: Property 'foo' is incompatible with index signature. +!!! error TS2345: Type 'Num' is not assignable to type 'Runtype'. + + interface Runtype { + constraint: Constraint + witness: A + } + + interface Num extends Runtype { + tag: 'number' + } + declare const Num: Num + + interface Obj }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {} + declare function Obj }>(fields: O): Obj; + + interface Constraint> extends Runtype { + underlying: A, + check: (x: A['witness']) => void, + } + \ No newline at end of file diff --git a/tests/baselines/reference/invariantGenericErrorElaboration.js b/tests/baselines/reference/invariantGenericErrorElaboration.js new file mode 100644 index 0000000000..253c4ab03b --- /dev/null +++ b/tests/baselines/reference/invariantGenericErrorElaboration.js @@ -0,0 +1,30 @@ +//// [invariantGenericErrorElaboration.ts] +// Repro from #19746 + +const wat: Runtype = Num; +const Foo = Obj({ foo: Num }) + +interface Runtype { + constraint: Constraint + witness: A +} + +interface Num extends Runtype { + tag: 'number' +} +declare const Num: Num + +interface Obj }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {} +declare function Obj }>(fields: O): Obj; + +interface Constraint> extends Runtype { + underlying: A, + check: (x: A['witness']) => void, +} + + +//// [invariantGenericErrorElaboration.js] +"use strict"; +// Repro from #19746 +var wat = Num; +var Foo = Obj({ foo: Num }); diff --git a/tests/baselines/reference/invariantGenericErrorElaboration.symbols b/tests/baselines/reference/invariantGenericErrorElaboration.symbols new file mode 100644 index 0000000000..9c141e5c27 --- /dev/null +++ b/tests/baselines/reference/invariantGenericErrorElaboration.symbols @@ -0,0 +1,76 @@ +=== tests/cases/compiler/invariantGenericErrorElaboration.ts === +// Repro from #19746 + +const wat: Runtype = Num; +>wat : Symbol(wat, Decl(invariantGenericErrorElaboration.ts, 2, 5)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13)) + +const Foo = Obj({ foo: Num }) +>Foo : Symbol(Foo, Decl(invariantGenericErrorElaboration.ts, 3, 5)) +>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111)) +>foo : Symbol(foo, Decl(invariantGenericErrorElaboration.ts, 3, 17)) +>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13)) + +interface Runtype { +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 5, 18)) + + constraint: Constraint +>constraint : Symbol(Runtype.constraint, Decl(invariantGenericErrorElaboration.ts, 5, 22)) +>Constraint : Symbol(Constraint, Decl(invariantGenericErrorElaboration.ts, 16, 81)) + + witness: A +>witness : Symbol(Runtype.witness, Decl(invariantGenericErrorElaboration.ts, 6, 30)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 5, 18)) +} + +interface Num extends Runtype { +>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) + + tag: 'number' +>tag : Symbol(Num.tag, Decl(invariantGenericErrorElaboration.ts, 10, 39)) +} +declare const Num: Num +>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13)) +>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13)) + +interface Obj }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {} +>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14)) +>_ : Symbol(_, Decl(invariantGenericErrorElaboration.ts, 15, 27)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>K : Symbol(K, Decl(invariantGenericErrorElaboration.ts, 15, 75)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14)) +>K : Symbol(K, Decl(invariantGenericErrorElaboration.ts, 15, 75)) + +declare function Obj }>(fields: O): Obj; +>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21)) +>_ : Symbol(_, Decl(invariantGenericErrorElaboration.ts, 16, 34)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>fields : Symbol(fields, Decl(invariantGenericErrorElaboration.ts, 16, 62)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21)) +>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111)) +>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21)) + +interface Constraint> extends Runtype { +>Constraint : Symbol(Constraint, Decl(invariantGenericErrorElaboration.ts, 16, 81)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21)) + + underlying: A, +>underlying : Symbol(Constraint.underlying, Decl(invariantGenericErrorElaboration.ts, 18, 76)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21)) + + check: (x: A['witness']) => void, +>check : Symbol(Constraint.check, Decl(invariantGenericErrorElaboration.ts, 19, 16)) +>x : Symbol(x, Decl(invariantGenericErrorElaboration.ts, 20, 10)) +>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21)) +} + diff --git a/tests/baselines/reference/invariantGenericErrorElaboration.types b/tests/baselines/reference/invariantGenericErrorElaboration.types new file mode 100644 index 0000000000..2120c06d5f --- /dev/null +++ b/tests/baselines/reference/invariantGenericErrorElaboration.types @@ -0,0 +1,78 @@ +=== tests/cases/compiler/invariantGenericErrorElaboration.ts === +// Repro from #19746 + +const wat: Runtype = Num; +>wat : Runtype +>Runtype : Runtype +>Num : Num + +const Foo = Obj({ foo: Num }) +>Foo : any +>Obj({ foo: Num }) : any +>Obj : ; }>(fields: O) => Obj +>{ foo: Num } : { foo: Num; } +>foo : Num +>Num : Num + +interface Runtype { +>Runtype : Runtype +>A : A + + constraint: Constraint +>constraint : Constraint +>Constraint : Constraint + + witness: A +>witness : A +>A : A +} + +interface Num extends Runtype { +>Num : Num +>Runtype : Runtype + + tag: 'number' +>tag : "number" +} +declare const Num: Num +>Num : Num +>Num : Num + +interface Obj }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {} +>Obj : Obj +>O : O +>_ : _ +>Runtype : Runtype +>Runtype : Runtype +>K : K +>O : O +>O : O +>K : K + +declare function Obj }>(fields: O): Obj; +>Obj : ; }>(fields: O) => Obj +>O : O +>_ : string +>Runtype : Runtype +>fields : O +>O : O +>Obj : Obj +>O : O + +interface Constraint> extends Runtype { +>Constraint : Constraint +>A : A +>Runtype : Runtype +>Runtype : Runtype +>A : A + + underlying: A, +>underlying : A +>A : A + + check: (x: A['witness']) => void, +>check : (x: A["witness"]) => void +>x : A["witness"] +>A : A +} + diff --git a/tests/cases/compiler/invariantGenericErrorElaboration.ts b/tests/cases/compiler/invariantGenericErrorElaboration.ts new file mode 100644 index 0000000000..6191949dd8 --- /dev/null +++ b/tests/cases/compiler/invariantGenericErrorElaboration.ts @@ -0,0 +1,24 @@ +// @strict: true + +// Repro from #19746 + +const wat: Runtype = Num; +const Foo = Obj({ foo: Num }) + +interface Runtype { + constraint: Constraint + witness: A +} + +interface Num extends Runtype { + tag: 'number' +} +declare const Num: Num + +interface Obj }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {} +declare function Obj }>(fields: O): Obj; + +interface Constraint> extends Runtype { + underlying: A, + check: (x: A['witness']) => void, +}