From 6a9e33779d3795b5c198a4af47f7397dd74060ee Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 3 May 2019 13:43:24 -0700 Subject: [PATCH] Add fix for circularity error triggered by deep signature return type comparisons with `this` types --- src/compiler/checker.ts | 6 +- ...ditionalOnMethodReturnOfGenericInstance.js | 59 +++++++++++++++++++ ...nalOnMethodReturnOfGenericInstance.symbols | 47 +++++++++++++++ ...ionalOnMethodReturnOfGenericInstance.types | 41 +++++++++++++ ...ditionalOnMethodReturnOfGenericInstance.ts | 18 ++++++ 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.js create mode 100644 tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.symbols create mode 100644 tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.types create mode 100644 tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c28d63dec766f..ae8ad618a2a82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 diff --git a/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.js b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.js new file mode 100644 index 0000000000000..c903864eb672a --- /dev/null +++ b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.js @@ -0,0 +1,59 @@ +//// [thisConditionalOnMethodReturnOfGenericInstance.ts] +class A { + unmeasurableUsage!: {[K in keyof T]-?: T[K]}; +} + +class B extends A { + method(): string | (this extends C ? undefined : null) { + return ""; + } +} + +class C extends B { + 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 diff --git a/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.symbols b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.symbols new file mode 100644 index 0000000000000..ca81357af143d --- /dev/null +++ b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.symbols @@ -0,0 +1,47 @@ +=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts === +class A { +>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 extends A { +>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 extends B { +>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)) + diff --git a/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.types b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.types new file mode 100644 index 0000000000000..8ec248f9d536a --- /dev/null +++ b/tests/baselines/reference/thisConditionalOnMethodReturnOfGenericInstance.types @@ -0,0 +1,41 @@ +=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts === +class A { +>A : A + + unmeasurableUsage!: {[K in keyof T]-?: T[K]}; +>unmeasurableUsage : { [K in keyof T]-?: T[K]; } +} + +class B extends A { +>B : B +>A : A + + method(): string | (this extends C ? undefined : null) { +>method : () => string | (this extends C ? undefined : null) +>null : null + + return ""; +>"" : "" + } +} + +class C extends B { +>C : C +>B : B + + 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 + diff --git a/tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts b/tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts new file mode 100644 index 0000000000000..a0264e90f4974 --- /dev/null +++ b/tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts @@ -0,0 +1,18 @@ +// @strict: true +class A { + unmeasurableUsage!: {[K in keyof T]-?: T[K]}; +} + +class B extends A { + method(): string | (this extends C ? undefined : null) { + return ""; + } +} + +class C extends B { + 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