Skip to content

Commit a3eadfe

Browse files
authored
Skip base type checks that can cause circularities (#44615)
* Don't do base type checks that can cause circularities * Add regression tests
1 parent d0159a8 commit a3eadfe

File tree

5 files changed

+216
-0
lines changed

5 files changed

+216
-0
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20082,6 +20082,14 @@ namespace ts {
2008220082
}
2008320083
(type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeCalculated;
2008420084
const target = (type as TypeReference).target as InterfaceType;
20085+
if (getObjectFlags(target) & ObjectFlags.Class) {
20086+
const baseTypeNode = getBaseTypeNodeOfClass(target);
20087+
// A base type expression may circularly reference the class itself (e.g. as an argument to function call), so we only
20088+
// check for base types specified as simple qualified names.
20089+
if (baseTypeNode && baseTypeNode.expression.kind !== SyntaxKind.Identifier && baseTypeNode.expression.kind !== SyntaxKind.PropertyAccessExpression) {
20090+
return undefined;
20091+
}
20092+
}
2008520093
const bases = getBaseTypes(target);
2008620094
if (bases.length !== 1) {
2008720095
return undefined;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//// [recursiveClassBaseType.ts]
2+
// Repro from #44281
3+
4+
declare const p: <T>(fn: () => T) => T;
5+
6+
declare const Base: <T>(val: T) => { new(): T };
7+
8+
class C extends Base({ x: p<C[]>(() => []) }) { }
9+
10+
// Repro from #44359
11+
12+
abstract class Base1 {
13+
abstract root(): Derived1;
14+
}
15+
16+
class Derived1 extends class extends Base1 {
17+
root() {
18+
return undefined as any;
19+
}
20+
}
21+
{ }
22+
23+
24+
//// [recursiveClassBaseType.js]
25+
"use strict";
26+
// Repro from #44281
27+
var __extends = (this && this.__extends) || (function () {
28+
var extendStatics = function (d, b) {
29+
extendStatics = Object.setPrototypeOf ||
30+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
31+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
32+
return extendStatics(d, b);
33+
};
34+
return function (d, b) {
35+
if (typeof b !== "function" && b !== null)
36+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
37+
extendStatics(d, b);
38+
function __() { this.constructor = d; }
39+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
40+
};
41+
})();
42+
var C = /** @class */ (function (_super) {
43+
__extends(C, _super);
44+
function C() {
45+
return _super !== null && _super.apply(this, arguments) || this;
46+
}
47+
return C;
48+
}(Base({ x: p(function () { return []; }) })));
49+
// Repro from #44359
50+
var Base1 = /** @class */ (function () {
51+
function Base1() {
52+
}
53+
return Base1;
54+
}());
55+
var Derived1 = /** @class */ (function (_super) {
56+
__extends(Derived1, _super);
57+
function Derived1() {
58+
return _super !== null && _super.apply(this, arguments) || this;
59+
}
60+
return Derived1;
61+
}(/** @class */ (function (_super) {
62+
__extends(class_1, _super);
63+
function class_1() {
64+
return _super !== null && _super.apply(this, arguments) || this;
65+
}
66+
class_1.prototype.root = function () {
67+
return undefined;
68+
};
69+
return class_1;
70+
}(Base1))));
71+
72+
73+
//// [recursiveClassBaseType.d.ts]
74+
declare const p: <T>(fn: () => T) => T;
75+
declare const Base: <T>(val: T) => {
76+
new (): T;
77+
};
78+
declare const C_base: new () => {
79+
x: C[];
80+
};
81+
declare class C extends C_base {
82+
}
83+
declare abstract class Base1 {
84+
abstract root(): Derived1;
85+
}
86+
declare const Derived1_base: {
87+
new (): {
88+
root(): any;
89+
};
90+
};
91+
declare class Derived1 extends Derived1_base {
92+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/recursiveClassBaseType.ts ===
2+
// Repro from #44281
3+
4+
declare const p: <T>(fn: () => T) => T;
5+
>p : Symbol(p, Decl(recursiveClassBaseType.ts, 2, 13))
6+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18))
7+
>fn : Symbol(fn, Decl(recursiveClassBaseType.ts, 2, 21))
8+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18))
9+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 2, 18))
10+
11+
declare const Base: <T>(val: T) => { new(): T };
12+
>Base : Symbol(Base, Decl(recursiveClassBaseType.ts, 4, 13))
13+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21))
14+
>val : Symbol(val, Decl(recursiveClassBaseType.ts, 4, 24))
15+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21))
16+
>T : Symbol(T, Decl(recursiveClassBaseType.ts, 4, 21))
17+
18+
class C extends Base({ x: p<C[]>(() => []) }) { }
19+
>C : Symbol(C, Decl(recursiveClassBaseType.ts, 4, 48))
20+
>Base : Symbol(Base, Decl(recursiveClassBaseType.ts, 4, 13))
21+
>x : Symbol(x, Decl(recursiveClassBaseType.ts, 6, 22))
22+
>p : Symbol(p, Decl(recursiveClassBaseType.ts, 2, 13))
23+
>C : Symbol(C, Decl(recursiveClassBaseType.ts, 4, 48))
24+
25+
// Repro from #44359
26+
27+
abstract class Base1 {
28+
>Base1 : Symbol(Base1, Decl(recursiveClassBaseType.ts, 6, 49))
29+
30+
abstract root(): Derived1;
31+
>root : Symbol(Base1.root, Decl(recursiveClassBaseType.ts, 10, 22))
32+
>Derived1 : Symbol(Derived1, Decl(recursiveClassBaseType.ts, 12, 1))
33+
}
34+
35+
class Derived1 extends class extends Base1 {
36+
>Derived1 : Symbol(Derived1, Decl(recursiveClassBaseType.ts, 12, 1))
37+
>Base1 : Symbol(Base1, Decl(recursiveClassBaseType.ts, 6, 49))
38+
39+
root() {
40+
>root : Symbol((Anonymous class).root, Decl(recursiveClassBaseType.ts, 14, 44))
41+
42+
return undefined as any;
43+
>undefined : Symbol(undefined)
44+
}
45+
}
46+
{ }
47+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/compiler/recursiveClassBaseType.ts ===
2+
// Repro from #44281
3+
4+
declare const p: <T>(fn: () => T) => T;
5+
>p : <T>(fn: () => T) => T
6+
>fn : () => T
7+
8+
declare const Base: <T>(val: T) => { new(): T };
9+
>Base : <T>(val: T) => new () => T
10+
>val : T
11+
12+
class C extends Base({ x: p<C[]>(() => []) }) { }
13+
>C : C
14+
>Base({ x: p<C[]>(() => []) }) : { x: C[]; }
15+
>Base : <T>(val: T) => new () => T
16+
>{ x: p<C[]>(() => []) } : { x: C[]; }
17+
>x : C[]
18+
>p<C[]>(() => []) : C[]
19+
>p : <T>(fn: () => T) => T
20+
>() => [] : () => never[]
21+
>[] : never[]
22+
23+
// Repro from #44359
24+
25+
abstract class Base1 {
26+
>Base1 : Base1
27+
28+
abstract root(): Derived1;
29+
>root : () => Derived1
30+
}
31+
32+
class Derived1 extends class extends Base1 {
33+
>Derived1 : Derived1
34+
>class extends Base1 { root() { return undefined as any; }} : (Anonymous class)
35+
>Base1 : Base1
36+
37+
root() {
38+
>root : () => any
39+
40+
return undefined as any;
41+
>undefined as any : any
42+
>undefined : undefined
43+
}
44+
}
45+
{ }
46+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// Repro from #44281
5+
6+
declare const p: <T>(fn: () => T) => T;
7+
8+
declare const Base: <T>(val: T) => { new(): T };
9+
10+
class C extends Base({ x: p<C[]>(() => []) }) { }
11+
12+
// Repro from #44359
13+
14+
abstract class Base1 {
15+
abstract root(): Derived1;
16+
}
17+
18+
class Derived1 extends class extends Base1 {
19+
root() {
20+
return undefined as any;
21+
}
22+
}
23+
{ }

0 commit comments

Comments
 (0)