Skip to content

Commit

Permalink
Merge pull request #19774 from Microsoft/fixInvariantGenericErrors
Browse files Browse the repository at this point in the history
Fix invariant generic error elaboration logic
  • Loading branch information
ahejlsberg authored Nov 6, 2017
2 parents 445001e + baafe51 commit 7a4808a
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9451,6 +9451,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].
Expand Down Expand Up @@ -9530,6 +9531,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((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
Expand Down Expand Up @@ -9561,6 +9563,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;
}
}
Expand Down Expand Up @@ -9599,8 +9605,11 @@ namespace ts {
}
}
if (result) {
errorInfo = saveErrorInfo;
return result;
if (!originalErrorInfo) {
errorInfo = saveErrorInfo;
return result;
}
errorInfo = originalErrorInfo;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
tests/cases/compiler/invariantGenericErrorElaboration.ts(3,7): error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
Types of property 'underlying' are incompatible.
Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
tests/cases/compiler/invariantGenericErrorElaboration.ts(4,17): error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype<any>; }'.
Property 'foo' is incompatible with index signature.
Type 'Num' is not assignable to type 'Runtype<any>'.


==== tests/cases/compiler/invariantGenericErrorElaboration.ts (2 errors) ====
// Repro from #19746

const wat: Runtype<any> = Num;
~~~
!!! error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
!!! error TS2322: Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
!!! error TS2322: Types of property 'underlying' are incompatible.
!!! error TS2322: Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
const Foo = Obj({ foo: Num })
~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype<any>; }'.
!!! error TS2345: Property 'foo' is incompatible with index signature.
!!! error TS2345: Type 'Num' is not assignable to type 'Runtype<any>'.

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}

30 changes: 30 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [invariantGenericErrorElaboration.ts]
// Repro from #19746

const wat: Runtype<any> = Num;
const Foo = Obj({ foo: Num })

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}


//// [invariantGenericErrorElaboration.js]
"use strict";
// Repro from #19746
var wat = Num;
var Foo = Obj({ foo: Num });
76 changes: 76 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
=== tests/cases/compiler/invariantGenericErrorElaboration.ts ===
// Repro from #19746

const wat: Runtype<any> = 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<A> {
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 5, 18))

constraint: Constraint<this>
>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<number> {
>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<O extends { [_ in string]: Runtype<any> }> 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<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
>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<A extends Runtype<any>> extends Runtype<A['witness']> {
>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))
}

78 changes: 78 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
=== tests/cases/compiler/invariantGenericErrorElaboration.ts ===
// Repro from #19746

const wat: Runtype<any> = Num;
>wat : Runtype<any>
>Runtype : Runtype<A>
>Num : Num

const Foo = Obj({ foo: Num })
>Foo : any
>Obj({ foo: Num }) : any
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>{ foo: Num } : { foo: Num; }
>foo : Num
>Num : Num

interface Runtype<A> {
>Runtype : Runtype<A>
>A : A

constraint: Constraint<this>
>constraint : Constraint<this>
>Constraint : Constraint<A>

witness: A
>witness : A
>A : A
}

interface Num extends Runtype<number> {
>Num : Num
>Runtype : Runtype<A>

tag: 'number'
>tag : "number"
}
declare const Num: Num
>Num : Num
>Num : Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
>Obj : Obj<O>
>O : O
>_ : _
>Runtype : Runtype<A>
>Runtype : Runtype<A>
>K : K
>O : O
>O : O
>K : K

declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>O : O
>_ : string
>Runtype : Runtype<A>
>fields : O
>O : O
>Obj : Obj<O>
>O : O

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
>Constraint : Constraint<A>
>A : A
>Runtype : Runtype<A>
>Runtype : Runtype<A>
>A : A

underlying: A,
>underlying : A
>A : A

check: (x: A['witness']) => void,
>check : (x: A["witness"]) => void
>x : A["witness"]
>A : A
}

24 changes: 24 additions & 0 deletions tests/cases/compiler/invariantGenericErrorElaboration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @strict: true

// Repro from #19746

const wat: Runtype<any> = Num;
const Foo = Obj({ foo: Num })

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}

0 comments on commit 7a4808a

Please sign in to comment.