From dc24f77741c5be2c48b69f8b816eff75ffcb04d2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 1 Dec 2017 15:00:07 -0800 Subject: [PATCH 1/4] Limit recursive structured type resolution --- src/compiler/checker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0b8ba29a71016..7897d5c0ece4b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6171,6 +6171,7 @@ namespace ts { function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { + setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type); From 7cfe6a4cdbed168fc670945a16b24cc3ae51ed93 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 1 Dec 2017 15:00:37 -0800 Subject: [PATCH 2/4] Test:Mutually recursive types do not recur infinitely --- .../mutuallyRecursiveInference.errors.txt | 10 ++++++++++ .../reference/mutuallyRecursiveInference.js | 7 +++++++ .../mutuallyRecursiveInference.symbols | 18 ++++++++++++++++++ .../reference/mutuallyRecursiveInference.types | 18 ++++++++++++++++++ .../compiler/mutuallyRecursiveInference.ts | 3 +++ 5 files changed, 56 insertions(+) create mode 100644 tests/baselines/reference/mutuallyRecursiveInference.errors.txt create mode 100644 tests/baselines/reference/mutuallyRecursiveInference.js create mode 100644 tests/baselines/reference/mutuallyRecursiveInference.symbols create mode 100644 tests/baselines/reference/mutuallyRecursiveInference.types create mode 100644 tests/cases/compiler/mutuallyRecursiveInference.ts diff --git a/tests/baselines/reference/mutuallyRecursiveInference.errors.txt b/tests/baselines/reference/mutuallyRecursiveInference.errors.txt new file mode 100644 index 0000000000000..ac6b66c7b5ca2 --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInference.errors.txt @@ -0,0 +1,10 @@ +tests/cases/compiler/mutuallyRecursiveInference.ts(2,27): error TS2536: Type '"a"' cannot be used to index type 'RT'. + + +==== tests/cases/compiler/mutuallyRecursiveInference.ts (1 errors) ==== + interface T { a: A } + interface L extends T {} + ~~~~~~~ +!!! error TS2536: Type '"a"' cannot be used to index type 'RT'. + interface X extends L {} + \ No newline at end of file diff --git a/tests/baselines/reference/mutuallyRecursiveInference.js b/tests/baselines/reference/mutuallyRecursiveInference.js new file mode 100644 index 0000000000000..1a12d8b3c2a5d --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInference.js @@ -0,0 +1,7 @@ +//// [mutuallyRecursiveInference.ts] +interface T { a: A } +interface L extends T {} +interface X extends L {} + + +//// [mutuallyRecursiveInference.js] diff --git a/tests/baselines/reference/mutuallyRecursiveInference.symbols b/tests/baselines/reference/mutuallyRecursiveInference.symbols new file mode 100644 index 0000000000000..e956811b48b90 --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInference.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/mutuallyRecursiveInference.ts === +interface T { a: A } +>T : Symbol(T, Decl(mutuallyRecursiveInference.ts, 0, 0)) +>A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 12)) +>a : Symbol(T.a, Decl(mutuallyRecursiveInference.ts, 0, 16)) +>A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 12)) + +interface L extends T {} +>L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 0, 23)) +>RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 1, 12)) +>T : Symbol(T, Decl(mutuallyRecursiveInference.ts, 0, 0)) +>RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 1, 12)) + +interface X extends L {} +>X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 1, 37)) +>L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 0, 23)) +>X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 1, 37)) + diff --git a/tests/baselines/reference/mutuallyRecursiveInference.types b/tests/baselines/reference/mutuallyRecursiveInference.types new file mode 100644 index 0000000000000..3c0b77e00bff3 --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInference.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/mutuallyRecursiveInference.ts === +interface T { a: A } +>T : T +>A : A +>a : A +>A : A + +interface L extends T {} +>L : L +>RT : RT +>T : T +>RT : RT + +interface X extends L {} +>X : X +>L : L +>X : X + diff --git a/tests/cases/compiler/mutuallyRecursiveInference.ts b/tests/cases/compiler/mutuallyRecursiveInference.ts new file mode 100644 index 0000000000000..41cef0f54c110 --- /dev/null +++ b/tests/cases/compiler/mutuallyRecursiveInference.ts @@ -0,0 +1,3 @@ +interface T { a: A } +interface L extends T {} +interface X extends L {} From 988f9ac641128cc3b201c0399332ca24fffa4bae Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 4 Dec 2017 13:29:51 -0800 Subject: [PATCH 3/4] Fill "empty" structured type marker with early symbols The same way that getResolvedMembersOrExportsOfSymbol does. That functionality may not be needed anymore, in fact. --- src/compiler/checker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7897d5c0ece4b..b608dd1c5e84c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6171,7 +6171,8 @@ namespace ts { function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { - setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); + const earlySymbols = (type.symbol && type.symbol.members) || emptySymbols; + setStructuredTypeMembers(type, earlySymbols, emptyArray, emptyArray, undefined, undefined); if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type); From fb3042c3f7a26cd62f6d3c0fa9dda1729e9212c1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 22 Dec 2017 14:39:25 -0800 Subject: [PATCH 4/4] Move recursion limiter to individual resolve* functions --- src/compiler/checker.ts | 4 +- tests/baselines/reference/dynamicNames.js | 20 ++++--- .../baselines/reference/dynamicNames.symbols | 56 ++++++++++++------- tests/baselines/reference/dynamicNames.types | 48 ++++++++++------ .../mutuallyRecursiveInference.errors.txt | 10 ---- .../reference/mutuallyRecursiveInference.js | 50 ++++++++++++++++- .../mutuallyRecursiveInference.symbols | 54 ++++++++++++++---- .../mutuallyRecursiveInference.types | 40 +++++++++++-- tests/cases/compiler/dynamicNames.ts | 17 +++--- .../compiler/mutuallyRecursiveInference.ts | 17 +++++- 10 files changed, 231 insertions(+), 85 deletions(-) delete mode 100644 tests/baselines/reference/mutuallyRecursiveInference.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b608dd1c5e84c..35babc132e9ff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5754,6 +5754,7 @@ namespace ts { if (source.symbol && members === getMembersOfSymbol(source.symbol)) { members = createSymbolTable(source.declaredProperties); } + setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); const thisArgument = lastOrUndefined(typeArguments); for (const baseType of baseTypes) { const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; @@ -6007,6 +6008,7 @@ namespace ts { if (symbol.exports) { members = getExportsOfSymbol(symbol); } + setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined); if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); const baseConstructorType = getBaseConstructorTypeOfClass(classType); @@ -6171,8 +6173,6 @@ namespace ts { function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { - const earlySymbols = (type.symbol && type.symbol.members) || emptySymbols; - setStructuredTypeMembers(type, earlySymbols, emptyArray, emptyArray, undefined, undefined); if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type); diff --git a/tests/baselines/reference/dynamicNames.js b/tests/baselines/reference/dynamicNames.js index 6582dab44d47d..89a5076fc3697 100644 --- a/tests/baselines/reference/dynamicNames.js +++ b/tests/baselines/reference/dynamicNames.js @@ -138,17 +138,21 @@ export const o1_s2 = o1[s2]; export const o2: T0 = o1; // recursive declarations -declare const rI: RI; -interface RI { - x: "a"; +// (type parameter indirection courtesy of #20400) +declare const rI: RI<"a">; +rI.x +interface RI { + x: T; [rI.x]: "b"; } -declare const rC: RC; -declare class RC { - x: "a"; +declare const rC: RC<"a">; +rC.x +declare class RC { + x: T; [rC.x]: "b"; -} +} + //// [module.js] "use strict"; @@ -205,6 +209,8 @@ exports.o1_c4 = exports.o1[exports.c4]; exports.o1_c5 = exports.o1[exports.c5]; exports.o1_s2 = exports.o1[exports.s2]; exports.o2 = exports.o1; +rI.x; +rC.x; //// [module.d.ts] diff --git a/tests/baselines/reference/dynamicNames.symbols b/tests/baselines/reference/dynamicNames.symbols index e030a9686e5e0..2f545c919e3ea 100644 --- a/tests/baselines/reference/dynamicNames.symbols +++ b/tests/baselines/reference/dynamicNames.symbols @@ -443,34 +443,50 @@ export const o2: T0 = o1; >o1 : Symbol(o1, Decl(main.ts, 101, 12)) // recursive declarations -declare const rI: RI; ->rI : Symbol(rI, Decl(main.ts, 115, 13)) ->RI : Symbol(RI, Decl(main.ts, 115, 21)) +// (type parameter indirection courtesy of #20400) +declare const rI: RI<"a">; +>rI : Symbol(rI, Decl(main.ts, 116, 13)) +>RI : Symbol(RI, Decl(main.ts, 117, 4)) -interface RI { ->RI : Symbol(RI, Decl(main.ts, 115, 21)) +rI.x +>rI.x : Symbol(RI.x, Decl(main.ts, 118, 35)) +>rI : Symbol(rI, Decl(main.ts, 116, 13)) +>x : Symbol(RI.x, Decl(main.ts, 118, 35)) - x: "a"; ->x : Symbol(RI.x, Decl(main.ts, 116, 14)) +interface RI { +>RI : Symbol(RI, Decl(main.ts, 117, 4)) +>T : Symbol(T, Decl(main.ts, 118, 13)) + + x: T; +>x : Symbol(RI.x, Decl(main.ts, 118, 35)) +>T : Symbol(T, Decl(main.ts, 118, 13)) [rI.x]: "b"; ->rI.x : Symbol(RI.x, Decl(main.ts, 116, 14)) ->rI : Symbol(rI, Decl(main.ts, 115, 13)) ->x : Symbol(RI.x, Decl(main.ts, 116, 14)) +>rI.x : Symbol(RI.x, Decl(main.ts, 118, 35)) +>rI : Symbol(rI, Decl(main.ts, 116, 13)) +>x : Symbol(RI.x, Decl(main.ts, 118, 35)) } -declare const rC: RC; ->rC : Symbol(rC, Decl(main.ts, 121, 13)) ->RC : Symbol(RC, Decl(main.ts, 121, 21)) +declare const rC: RC<"a">; +>rC : Symbol(rC, Decl(main.ts, 123, 13)) +>RC : Symbol(RC, Decl(main.ts, 124, 4)) + +rC.x +>rC.x : Symbol(RC.x, Decl(main.ts, 125, 39)) +>rC : Symbol(rC, Decl(main.ts, 123, 13)) +>x : Symbol(RC.x, Decl(main.ts, 125, 39)) -declare class RC { ->RC : Symbol(RC, Decl(main.ts, 121, 21)) +declare class RC { +>RC : Symbol(RC, Decl(main.ts, 124, 4)) +>T : Symbol(T, Decl(main.ts, 125, 17)) - x: "a"; ->x : Symbol(RC.x, Decl(main.ts, 122, 18)) + x: T; +>x : Symbol(RC.x, Decl(main.ts, 125, 39)) +>T : Symbol(T, Decl(main.ts, 125, 17)) [rC.x]: "b"; ->rC.x : Symbol(RC.x, Decl(main.ts, 122, 18)) ->rC : Symbol(rC, Decl(main.ts, 121, 13)) ->x : Symbol(RC.x, Decl(main.ts, 122, 18)) +>rC.x : Symbol(RC.x, Decl(main.ts, 125, 39)) +>rC : Symbol(rC, Decl(main.ts, 123, 13)) +>x : Symbol(RC.x, Decl(main.ts, 125, 39)) } + diff --git a/tests/baselines/reference/dynamicNames.types b/tests/baselines/reference/dynamicNames.types index b2125d673069c..f3c664579116e 100644 --- a/tests/baselines/reference/dynamicNames.types +++ b/tests/baselines/reference/dynamicNames.types @@ -519,34 +519,50 @@ export const o2: T0 = o1; >o1 : { [c4]: number; [c5]: string; [s2]: boolean; } // recursive declarations -declare const rI: RI; ->rI : RI ->RI : RI +// (type parameter indirection courtesy of #20400) +declare const rI: RI<"a">; +>rI : RI<"a"> +>RI : RI -interface RI { ->RI : RI - - x: "a"; +rI.x +>rI.x : "a" +>rI : RI<"a"> >x : "a" +interface RI { +>RI : RI +>T : T + + x: T; +>x : T +>T : T + [rI.x]: "b"; >rI.x : "a" ->rI : RI +>rI : RI<"a"> >x : "a" } -declare const rC: RC; ->rC : RC ->RC : RC - -declare class RC { ->RC : RC +declare const rC: RC<"a">; +>rC : RC<"a"> +>RC : RC - x: "a"; +rC.x +>rC.x : "a" +>rC : RC<"a"> >x : "a" +declare class RC { +>RC : RC +>T : T + + x: T; +>x : T +>T : T + [rC.x]: "b"; >rC.x : "a" ->rC : RC +>rC : RC<"a"> >x : "a" } + diff --git a/tests/baselines/reference/mutuallyRecursiveInference.errors.txt b/tests/baselines/reference/mutuallyRecursiveInference.errors.txt deleted file mode 100644 index ac6b66c7b5ca2..0000000000000 --- a/tests/baselines/reference/mutuallyRecursiveInference.errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -tests/cases/compiler/mutuallyRecursiveInference.ts(2,27): error TS2536: Type '"a"' cannot be used to index type 'RT'. - - -==== tests/cases/compiler/mutuallyRecursiveInference.ts (1 errors) ==== - interface T { a: A } - interface L extends T {} - ~~~~~~~ -!!! error TS2536: Type '"a"' cannot be used to index type 'RT'. - interface X extends L {} - \ No newline at end of file diff --git a/tests/baselines/reference/mutuallyRecursiveInference.js b/tests/baselines/reference/mutuallyRecursiveInference.js index 1a12d8b3c2a5d..f8bb67954b8ec 100644 --- a/tests/baselines/reference/mutuallyRecursiveInference.js +++ b/tests/baselines/reference/mutuallyRecursiveInference.js @@ -1,7 +1,51 @@ //// [mutuallyRecursiveInference.ts] -interface T { a: A } -interface L extends T {} -interface X extends L {} +class T { + a: A; + b: any +} +class L extends T { + m() { this.a } +} +class X extends L { + a: 'a' | 'b' + b: number + m2() { + this.a + } +} //// [mutuallyRecursiveInference.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var T = /** @class */ (function () { + function T() { + } + return T; +}()); +var L = /** @class */ (function (_super) { + __extends(L, _super); + function L() { + return _super !== null && _super.apply(this, arguments) || this; + } + L.prototype.m = function () { this.a; }; + return L; +}(T)); +var X = /** @class */ (function (_super) { + __extends(X, _super); + function X() { + return _super !== null && _super.apply(this, arguments) || this; + } + X.prototype.m2 = function () { + this.a; + }; + return X; +}(L)); diff --git a/tests/baselines/reference/mutuallyRecursiveInference.symbols b/tests/baselines/reference/mutuallyRecursiveInference.symbols index e956811b48b90..ed4768a2e5c4b 100644 --- a/tests/baselines/reference/mutuallyRecursiveInference.symbols +++ b/tests/baselines/reference/mutuallyRecursiveInference.symbols @@ -1,18 +1,48 @@ === tests/cases/compiler/mutuallyRecursiveInference.ts === -interface T { a: A } +class T { >T : Symbol(T, Decl(mutuallyRecursiveInference.ts, 0, 0)) ->A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 12)) ->a : Symbol(T.a, Decl(mutuallyRecursiveInference.ts, 0, 16)) ->A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 12)) +>A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 8)) -interface L extends T {} ->L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 0, 23)) ->RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 1, 12)) + a: A; +>a : Symbol(T.a, Decl(mutuallyRecursiveInference.ts, 0, 12)) +>A : Symbol(A, Decl(mutuallyRecursiveInference.ts, 0, 8)) + + b: any +>b : Symbol(T.b, Decl(mutuallyRecursiveInference.ts, 1, 9)) +} +class L extends T { +>L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 3, 1)) +>RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 4, 8)) +>a : Symbol(a, Decl(mutuallyRecursiveInference.ts, 4, 20)) +>b : Symbol(b, Decl(mutuallyRecursiveInference.ts, 4, 34)) >T : Symbol(T, Decl(mutuallyRecursiveInference.ts, 0, 0)) ->RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 1, 12)) +>RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 4, 8)) +>RT : Symbol(RT, Decl(mutuallyRecursiveInference.ts, 4, 8)) + + m() { this.a } +>m : Symbol(L.m, Decl(mutuallyRecursiveInference.ts, 4, 69)) +>this.a : Symbol(T.a, Decl(mutuallyRecursiveInference.ts, 0, 12)) +>this : Symbol(L, Decl(mutuallyRecursiveInference.ts, 3, 1)) +>a : Symbol(T.a, Decl(mutuallyRecursiveInference.ts, 0, 12)) +} +class X extends L { +>X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 6, 1)) +>L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 3, 1)) +>X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 6, 1)) + + a: 'a' | 'b' +>a : Symbol(X.a, Decl(mutuallyRecursiveInference.ts, 7, 22)) + + b: number +>b : Symbol(X.b, Decl(mutuallyRecursiveInference.ts, 8, 16)) + + m2() { +>m2 : Symbol(X.m2, Decl(mutuallyRecursiveInference.ts, 9, 13)) -interface X extends L {} ->X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 1, 37)) ->L : Symbol(L, Decl(mutuallyRecursiveInference.ts, 0, 23)) ->X : Symbol(X, Decl(mutuallyRecursiveInference.ts, 1, 37)) + this.a +>this.a : Symbol(X.a, Decl(mutuallyRecursiveInference.ts, 7, 22)) +>this : Symbol(X, Decl(mutuallyRecursiveInference.ts, 6, 1)) +>a : Symbol(X.a, Decl(mutuallyRecursiveInference.ts, 7, 22)) + } +} diff --git a/tests/baselines/reference/mutuallyRecursiveInference.types b/tests/baselines/reference/mutuallyRecursiveInference.types index 3c0b77e00bff3..8839d82867365 100644 --- a/tests/baselines/reference/mutuallyRecursiveInference.types +++ b/tests/baselines/reference/mutuallyRecursiveInference.types @@ -1,18 +1,48 @@ === tests/cases/compiler/mutuallyRecursiveInference.ts === -interface T { a: A } +class T { >T : T >A : A + + a: A; >a : A >A : A -interface L extends T {} + b: any +>b : any +} +class L extends T { >L : L >RT : RT ->T : T +>a : "a" | "b" +>b : any +>T : T +>RT : RT >RT : RT -interface X extends L {} + m() { this.a } +>m : () => void +>this.a : RT[RT["a"]] +>this : this +>a : RT[RT["a"]] +} +class X extends L { >X : X ->L : L +>L : L >X : X + a: 'a' | 'b' +>a : "a" | "b" + + b: number +>b : number + + m2() { +>m2 : () => void + + this.a +>this.a : "a" | "b" +>this : this +>a : "a" | "b" + } +} + diff --git a/tests/cases/compiler/dynamicNames.ts b/tests/cases/compiler/dynamicNames.ts index 9562685b08f7a..3380dc8d9def7 100644 --- a/tests/cases/compiler/dynamicNames.ts +++ b/tests/cases/compiler/dynamicNames.ts @@ -140,14 +140,17 @@ export const o1_s2 = o1[s2]; export const o2: T0 = o1; // recursive declarations -declare const rI: RI; -interface RI { - x: "a"; +// (type parameter indirection courtesy of #20400) +declare const rI: RI<"a">; +rI.x +interface RI { + x: T; [rI.x]: "b"; } -declare const rC: RC; -declare class RC { - x: "a"; +declare const rC: RC<"a">; +rC.x +declare class RC { + x: T; [rC.x]: "b"; -} \ No newline at end of file +} diff --git a/tests/cases/compiler/mutuallyRecursiveInference.ts b/tests/cases/compiler/mutuallyRecursiveInference.ts index 41cef0f54c110..0f80d6c337d94 100644 --- a/tests/cases/compiler/mutuallyRecursiveInference.ts +++ b/tests/cases/compiler/mutuallyRecursiveInference.ts @@ -1,3 +1,14 @@ -interface T { a: A } -interface L extends T {} -interface X extends L {} +class T { + a: A; + b: any +} +class L extends T { + m() { this.a } +} +class X extends L { + a: 'a' | 'b' + b: number + m2() { + this.a + } +}