Skip to content

Commit 6a9e337

Browse files
committed
Add fix for circularity error triggered by deep signature return type comparisons with this types
1 parent 18e5656 commit 6a9e337

5 files changed

+169
-2
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12024,12 +12024,14 @@ namespace ts {
1202412024
}
1202512025

1202612026
if (!ignoreReturnTypes) {
12027-
const targetReturnType = (target.declaration && isJSConstructor(target.declaration)) ?
12027+
// If a signature reolution is already in-flight, skip issuing a circularity error
12028+
// here and just use the `any` type directly
12029+
const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType : (target.declaration && isJSConstructor(target.declaration)) ?
1202812030
getJSClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
1202912031
if (targetReturnType === voidType) {
1203012032
return result;
1203112033
}
12032-
const sourceReturnType = (source.declaration && isJSConstructor(source.declaration)) ?
12034+
const sourceReturnType = isResolvingReturnTypeOfSignature(source) ? anyType : (source.declaration && isJSConstructor(source.declaration)) ?
1203312035
getJSClassType(source.declaration.symbol)! : getReturnTypeOfSignature(source);
1203412036

1203512037
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//// [thisConditionalOnMethodReturnOfGenericInstance.ts]
2+
class A<T> {
3+
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
4+
}
5+
6+
class B<T> extends A<T> {
7+
method(): string | (this extends C ? undefined : null) {
8+
return "";
9+
}
10+
}
11+
12+
class C<T = any> extends B<T> {
13+
marker!: string;
14+
}
15+
16+
const x = new C<{}>();
17+
18+
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
19+
20+
21+
//// [thisConditionalOnMethodReturnOfGenericInstance.js]
22+
"use strict";
23+
var __extends = (this && this.__extends) || (function () {
24+
var extendStatics = function (d, b) {
25+
extendStatics = Object.setPrototypeOf ||
26+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
27+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
28+
return extendStatics(d, b);
29+
};
30+
return function (d, b) {
31+
extendStatics(d, b);
32+
function __() { this.constructor = d; }
33+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
34+
};
35+
})();
36+
var A = /** @class */ (function () {
37+
function A() {
38+
}
39+
return A;
40+
}());
41+
var B = /** @class */ (function (_super) {
42+
__extends(B, _super);
43+
function B() {
44+
return _super !== null && _super.apply(this, arguments) || this;
45+
}
46+
B.prototype.method = function () {
47+
return "";
48+
};
49+
return B;
50+
}(A));
51+
var C = /** @class */ (function (_super) {
52+
__extends(C, _super);
53+
function C() {
54+
return _super !== null && _super.apply(this, arguments) || this;
55+
}
56+
return C;
57+
}(B));
58+
var x = new C();
59+
var y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
2+
class A<T> {
3+
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
4+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
5+
6+
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
7+
>unmeasurableUsage : Symbol(A.unmeasurableUsage, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 12))
8+
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
9+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
10+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
11+
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
12+
}
13+
14+
class B<T> extends A<T> {
15+
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
16+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))
17+
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
18+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))
19+
20+
method(): string | (this extends C ? undefined : null) {
21+
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
22+
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
23+
24+
return "";
25+
}
26+
}
27+
28+
class C<T = any> extends B<T> {
29+
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
30+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))
31+
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
32+
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))
33+
34+
marker!: string;
35+
>marker : Symbol(C.marker, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 31))
36+
}
37+
38+
const x = new C<{}>();
39+
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
40+
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
41+
42+
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
43+
>y : Symbol(y, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 16, 5))
44+
>x.method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
45+
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
46+
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
47+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
2+
class A<T> {
3+
>A : A<T>
4+
5+
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
6+
>unmeasurableUsage : { [K in keyof T]-?: T[K]; }
7+
}
8+
9+
class B<T> extends A<T> {
10+
>B : B<T>
11+
>A : A<T>
12+
13+
method(): string | (this extends C ? undefined : null) {
14+
>method : () => string | (this extends C<any> ? undefined : null)
15+
>null : null
16+
17+
return "";
18+
>"" : ""
19+
}
20+
}
21+
22+
class C<T = any> extends B<T> {
23+
>C : C<T>
24+
>B : B<T>
25+
26+
marker!: string;
27+
>marker : string
28+
}
29+
30+
const x = new C<{}>();
31+
>x : C<{}>
32+
>new C<{}>() : C<{}>
33+
>C : typeof C
34+
35+
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
36+
>y : string | undefined
37+
>x.method() : string | undefined
38+
>x.method : () => string | undefined
39+
>x : C<{}>
40+
>method : () => string | undefined
41+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @strict: true
2+
class A<T> {
3+
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
4+
}
5+
6+
class B<T> extends A<T> {
7+
method(): string | (this extends C ? undefined : null) {
8+
return "";
9+
}
10+
}
11+
12+
class C<T = any> extends B<T> {
13+
marker!: string;
14+
}
15+
16+
const x = new C<{}>();
17+
18+
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type

0 commit comments

Comments
 (0)