Skip to content

Commit

Permalink
Add fix for circularity error triggered by deep signature return type…
Browse files Browse the repository at this point in the history
… comparisons with `this` types
  • Loading branch information
weswigham committed May 3, 2019
1 parent 18e5656 commit 6a9e337
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/compiler/checker.ts
Expand Up @@ -12024,12 +12024,14 @@ namespace ts {
}

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

// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
Expand Down
@@ -0,0 +1,59 @@
//// [thisConditionalOnMethodReturnOfGenericInstance.ts]
class A<T> {
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
}

class B<T> extends A<T> {
method(): string | (this extends C ? undefined : null) {
return "";
}
}

class C<T = any> extends B<T> {
marker!: string;
}

const x = new C<{}>();

const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type


//// [thisConditionalOnMethodReturnOfGenericInstance.js]
"use strict";
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 (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
B.prototype.method = function () {
return "";
};
return B;
}(A));
var C = /** @class */ (function (_super) {
__extends(C, _super);
function C() {
return _super !== null && _super.apply(this, arguments) || this;
}
return C;
}(B));
var x = new C();
var y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
@@ -0,0 +1,47 @@
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
class A<T> {
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))

unmeasurableUsage!: {[K in keyof T]-?: T[K]};
>unmeasurableUsage : Symbol(A.unmeasurableUsage, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 12))
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
}

class B<T> extends A<T> {
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))

method(): string | (this extends C ? undefined : null) {
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))

return "";
}
}

class C<T = any> extends B<T> {
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))

marker!: string;
>marker : Symbol(C.marker, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 31))
}

const x = new C<{}>();
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))

const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
>y : Symbol(y, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 16, 5))
>x.method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))

@@ -0,0 +1,41 @@
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
class A<T> {
>A : A<T>

unmeasurableUsage!: {[K in keyof T]-?: T[K]};
>unmeasurableUsage : { [K in keyof T]-?: T[K]; }
}

class B<T> extends A<T> {
>B : B<T>
>A : A<T>

method(): string | (this extends C ? undefined : null) {
>method : () => string | (this extends C<any> ? undefined : null)
>null : null

return "";
>"" : ""
}
}

class C<T = any> extends B<T> {
>C : C<T>
>B : B<T>

marker!: string;
>marker : string
}

const x = new C<{}>();
>x : C<{}>
>new C<{}>() : C<{}>
>C : typeof C

const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
>y : string | undefined
>x.method() : string | undefined
>x.method : () => string | undefined
>x : C<{}>
>method : () => string | undefined

@@ -0,0 +1,18 @@
// @strict: true
class A<T> {
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
}

class B<T> extends A<T> {
method(): string | (this extends C ? undefined : null) {
return "";
}
}

class C<T = any> extends B<T> {
marker!: string;
}

const x = new C<{}>();

const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type

0 comments on commit 6a9e337

Please sign in to comment.