From 841ceb4649bd5b4ac431bc2d68f7c413b9a58ee0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 16 Jun 2021 08:49:27 -0700 Subject: [PATCH 1/2] Don't do base type checks that can cause circularities --- src/compiler/checker.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 533bedc70a1cd..c57469dcc5cc5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20082,6 +20082,14 @@ namespace ts { } (type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeCalculated; const target = (type as TypeReference).target as InterfaceType; + if (getObjectFlags(target) & ObjectFlags.Class) { + const baseTypeNode = getBaseTypeNodeOfClass(target); + // A base type expression may circularly reference the class itself (e.g. as an argument to function call), so we only + // check for base types specified as simple qualified names. + if (baseTypeNode && baseTypeNode.expression.kind !== SyntaxKind.Identifier && baseTypeNode.expression.kind !== SyntaxKind.PropertyAccessExpression) { + return undefined; + } + } const bases = getBaseTypes(target); if (bases.length !== 1) { return undefined; From e9f29b636b2a1e06eb73cf2422aac3db6fd1ccc2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 16 Jun 2021 08:58:20 -0700 Subject: [PATCH 2/2] Add regression tests --- .../reference/recursiveClassBaseType.js | 92 +++++++++++++++++++ .../reference/recursiveClassBaseType.symbols | 47 ++++++++++ .../reference/recursiveClassBaseType.types | 46 ++++++++++ .../cases/compiler/recursiveClassBaseType.ts | 23 +++++ 4 files changed, 208 insertions(+) create mode 100644 tests/baselines/reference/recursiveClassBaseType.js create mode 100644 tests/baselines/reference/recursiveClassBaseType.symbols create mode 100644 tests/baselines/reference/recursiveClassBaseType.types create mode 100644 tests/cases/compiler/recursiveClassBaseType.ts diff --git a/tests/baselines/reference/recursiveClassBaseType.js b/tests/baselines/reference/recursiveClassBaseType.js new file mode 100644 index 0000000000000..15b173e3caca3 --- /dev/null +++ b/tests/baselines/reference/recursiveClassBaseType.js @@ -0,0 +1,92 @@ +//// [recursiveClassBaseType.ts] +// Repro from #44281 + +declare const p: (fn: () => T) => T; + +declare const Base: (val: T) => { new(): T }; + +class C extends Base({ x: p(() => []) }) { } + +// Repro from #44359 + +abstract class Base1 { + abstract root(): Derived1; +} + +class Derived1 extends class extends Base1 { + root() { + return undefined as any; + } +} +{ } + + +//// [recursiveClassBaseType.js] +"use strict"; +// Repro from #44281 +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var C = /** @class */ (function (_super) { + __extends(C, _super); + function C() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C; +}(Base({ x: p(function () { return []; }) }))); +// Repro from #44359 +var Base1 = /** @class */ (function () { + function Base1() { + } + return Base1; +}()); +var Derived1 = /** @class */ (function (_super) { + __extends(Derived1, _super); + function Derived1() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Derived1; +}(/** @class */ (function (_super) { + __extends(class_1, _super); + function class_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + class_1.prototype.root = function () { + return undefined; + }; + return class_1; +}(Base1)))); + + +//// [recursiveClassBaseType.d.ts] +declare const p: (fn: () => T) => T; +declare const Base: (val: T) => { + new (): T; +}; +declare const C_base: new () => { + x: C[]; +}; +declare class C extends C_base { +} +declare abstract class Base1 { + abstract root(): Derived1; +} +declare const Derived1_base: { + new (): { + root(): any; + }; +}; +declare class Derived1 extends Derived1_base { +} diff --git a/tests/baselines/reference/recursiveClassBaseType.symbols b/tests/baselines/reference/recursiveClassBaseType.symbols new file mode 100644 index 0000000000000..5e41d738d2639 --- /dev/null +++ b/tests/baselines/reference/recursiveClassBaseType.symbols @@ -0,0 +1,47 @@ +=== tests/cases/compiler/recursiveClassBaseType.ts === +// Repro from #44281 + +declare const p: (fn: () => T) => T; +>p : Symbol(p, Decl(recursiveClassBaseType.ts, 2, 13)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18)) +>fn : Symbol(fn, Decl(recursiveClassBaseType.ts, 2, 21)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18)) + +declare const Base: (val: T) => { new(): T }; +>Base : Symbol(Base, Decl(recursiveClassBaseType.ts, 4, 13)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21)) +>val : Symbol(val, Decl(recursiveClassBaseType.ts, 4, 24)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21)) +>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21)) + +class C extends Base({ x: p(() => []) }) { } +>C : Symbol(C, Decl(recursiveClassBaseType.ts, 4, 48)) +>Base : Symbol(Base, Decl(recursiveClassBaseType.ts, 4, 13)) +>x : Symbol(x, Decl(recursiveClassBaseType.ts, 6, 22)) +>p : Symbol(p, Decl(recursiveClassBaseType.ts, 2, 13)) +>C : Symbol(C, Decl(recursiveClassBaseType.ts, 4, 48)) + +// Repro from #44359 + +abstract class Base1 { +>Base1 : Symbol(Base1, Decl(recursiveClassBaseType.ts, 6, 49)) + + abstract root(): Derived1; +>root : Symbol(Base1.root, Decl(recursiveClassBaseType.ts, 10, 22)) +>Derived1 : Symbol(Derived1, Decl(recursiveClassBaseType.ts, 12, 1)) +} + +class Derived1 extends class extends Base1 { +>Derived1 : Symbol(Derived1, Decl(recursiveClassBaseType.ts, 12, 1)) +>Base1 : Symbol(Base1, Decl(recursiveClassBaseType.ts, 6, 49)) + + root() { +>root : Symbol((Anonymous class).root, Decl(recursiveClassBaseType.ts, 14, 44)) + + return undefined as any; +>undefined : Symbol(undefined) + } +} +{ } + diff --git a/tests/baselines/reference/recursiveClassBaseType.types b/tests/baselines/reference/recursiveClassBaseType.types new file mode 100644 index 0000000000000..46ca8433eddb5 --- /dev/null +++ b/tests/baselines/reference/recursiveClassBaseType.types @@ -0,0 +1,46 @@ +=== tests/cases/compiler/recursiveClassBaseType.ts === +// Repro from #44281 + +declare const p: (fn: () => T) => T; +>p : (fn: () => T) => T +>fn : () => T + +declare const Base: (val: T) => { new(): T }; +>Base : (val: T) => new () => T +>val : T + +class C extends Base({ x: p(() => []) }) { } +>C : C +>Base({ x: p(() => []) }) : { x: C[]; } +>Base : (val: T) => new () => T +>{ x: p(() => []) } : { x: C[]; } +>x : C[] +>p(() => []) : C[] +>p : (fn: () => T) => T +>() => [] : () => never[] +>[] : never[] + +// Repro from #44359 + +abstract class Base1 { +>Base1 : Base1 + + abstract root(): Derived1; +>root : () => Derived1 +} + +class Derived1 extends class extends Base1 { +>Derived1 : Derived1 +>class extends Base1 { root() { return undefined as any; }} : (Anonymous class) +>Base1 : Base1 + + root() { +>root : () => any + + return undefined as any; +>undefined as any : any +>undefined : undefined + } +} +{ } + diff --git a/tests/cases/compiler/recursiveClassBaseType.ts b/tests/cases/compiler/recursiveClassBaseType.ts new file mode 100644 index 0000000000000..a79be0e0fe65f --- /dev/null +++ b/tests/cases/compiler/recursiveClassBaseType.ts @@ -0,0 +1,23 @@ +// @strict: true +// @declaration: true + +// Repro from #44281 + +declare const p: (fn: () => T) => T; + +declare const Base: (val: T) => { new(): T }; + +class C extends Base({ x: p(() => []) }) { } + +// Repro from #44359 + +abstract class Base1 { + abstract root(): Derived1; +} + +class Derived1 extends class extends Base1 { + root() { + return undefined as any; + } +} +{ }