Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
92 changes: 92 additions & 0 deletions tests/baselines/reference/recursiveClassBaseType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//// [recursiveClassBaseType.ts]
// Repro from #44281

declare const p: <T>(fn: () => T) => T;

declare const Base: <T>(val: T) => { new(): T };

class C extends Base({ x: p<C[]>(() => []) }) { }

// 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: <T>(fn: () => T) => T;
declare const Base: <T>(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 {
}
47 changes: 47 additions & 0 deletions tests/baselines/reference/recursiveClassBaseType.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
=== tests/cases/compiler/recursiveClassBaseType.ts ===
// Repro from #44281

declare const p: <T>(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: <T>(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[]>(() => []) }) { }
>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)
}
}
{ }

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

declare const p: <T>(fn: () => T) => T;
>p : <T>(fn: () => T) => T
>fn : () => T

declare const Base: <T>(val: T) => { new(): T };
>Base : <T>(val: T) => new () => T
>val : T

class C extends Base({ x: p<C[]>(() => []) }) { }
>C : C
>Base({ x: p<C[]>(() => []) }) : { x: C[]; }
>Base : <T>(val: T) => new () => T
>{ x: p<C[]>(() => []) } : { x: C[]; }
>x : C[]
>p<C[]>(() => []) : C[]
>p : <T>(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
}
}
{ }

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

// Repro from #44281

declare const p: <T>(fn: () => T) => T;

declare const Base: <T>(val: T) => { new(): T };

class C extends Base({ x: p<C[]>(() => []) }) { }

// Repro from #44359

abstract class Base1 {
abstract root(): Derived1;
}

class Derived1 extends class extends Base1 {
root() {
return undefined as any;
}
}
{ }