Skip to content

Fix circular return type issue #26236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 6, 2018
184 changes: 87 additions & 97 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,10 @@
"category": "Error",
"code": 2576
},
"Return type annotation circularly references itself.": {
"category": "Error",
"code": 2577
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
Expand Down
7 changes: 3 additions & 4 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3374,10 +3374,9 @@ namespace ts {
* JavaScript file, gets the return type annotation from JSDoc.
*/
export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
if (isJSDocSignature(node)) {
return node.type && node.type.typeExpression && node.type.typeExpression.type;
}
return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
return isJSDocSignature(node) ?
node.type && node.type.typeExpression && node.type.typeExpression.type :
node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
}

export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(7,23): error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
Type 'Thenable' is not assignable to type 'PromiseLike<T>'.
Types of property 'then' are incompatible.
Expand All @@ -12,7 +13,7 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(23,25): error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.


==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (8 errors) ====
==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (9 errors) ====
declare class Thenable { then(): void; }
declare let a: any;
declare let obj: { then: string; };
Expand All @@ -21,19 +22,21 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
async function fn2(): { } { } // error
~~~
!!! error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
async function fn3(): any { } // error
~~~
!!! error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
async function fn4(): number { } // error
~~~~~~
!!! error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
~~~~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
async function fn5(): PromiseLike<void> { } // error
~~~~~~~~~~~~~~~~~
!!! error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
async function fn6(): Thenable { } // error
~~~~~~~~
!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
~~~~~~~~
!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
!!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike<T>'.
!!! error TS1055: Types of property 'then' are incompatible.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(6,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(6,23): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(7,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(8,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(8,23): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(9,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(10,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(17,16): error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(23,25): error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.


==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ====
==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (9 errors) ====
declare class Thenable { then(): void; }
declare let a: any;
declare let obj: { then: string; };
Expand All @@ -16,12 +18,16 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
async function fn2(): { } { } // error
~~~
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
async function fn3(): any { } // error
~~~
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
async function fn4(): number { } // error
~~~~~~
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
~~~~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
async function fn5(): PromiseLike<void> { } // error
~~~~~~~~~~~~~~~~~
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/checkJsdocTypeTag6.types
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ var g = function (prop) {

/** @type {(a: number) => number} */
function add1(a, b) { return a + b; }
>add1 : (a: any, b: any) => number
>a : any
>add1 : (a: number, b: any) => number
>a : number
>b : any
>a + b : any
>a : any
>a : number
>b : any

/** @type {(a: number, b: number) => number} */
Expand Down
34 changes: 33 additions & 1 deletion tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarO
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(2,5): error TS2502: 'varOfAliasedType1' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(4,5): error TS2502: 'varOfAliasedType2' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(5,6): error TS2456: Type alias 'typeAlias2' circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(7,18): error TS2577: Return type annotation circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(9,6): error TS2456: Type alias 'typeAlias3' circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(18,6): error TS2456: Type alias 'R' circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(19,29): error TS2577: Return type annotation circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(25,6): error TS2456: Type alias 'R2' circularly references itself.
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(26,15): error TS2577: Return type annotation circularly references itself.


==== tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts (5 errors) ====
==== tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts (10 errors) ====
type typeAlias1 = typeof varOfAliasedType1;
~~~~~~~~~~
!!! error TS2456: Type alias 'typeAlias1' circularly references itself.
Expand All @@ -21,8 +26,35 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarO
!!! error TS2456: Type alias 'typeAlias2' circularly references itself.

function func(): typeAlias3 { return null; }
~~~~~~~~~~
!!! error TS2577: Return type annotation circularly references itself.
var varOfAliasedType3 = func();
type typeAlias3 = typeof varOfAliasedType3;
~~~~~~~~~~
!!! error TS2456: Type alias 'typeAlias3' circularly references itself.

// Repro from #26104

interface Input {
a: number;
b: number;
}

type R = ReturnType<typeof mul>;
~
!!! error TS2456: Type alias 'R' circularly references itself.
function mul(input: Input): R {
~
!!! error TS2577: Return type annotation circularly references itself.
return input.a * input.b;
}

// Repro from #26104

type R2 = ReturnType<typeof f>;
~~
!!! error TS2456: Type alias 'R2' circularly references itself.
function f(): R2 { return 0; }
~~
!!! error TS2577: Return type annotation circularly references itself.

21 changes: 21 additions & 0 deletions tests/baselines/reference/circularTypeofWithVarOrFunc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,31 @@ type typeAlias2 = typeof varOfAliasedType2;
function func(): typeAlias3 { return null; }
var varOfAliasedType3 = func();
type typeAlias3 = typeof varOfAliasedType3;

// Repro from #26104

interface Input {
a: number;
b: number;
}

type R = ReturnType<typeof mul>;
function mul(input: Input): R {
return input.a * input.b;
}

// Repro from #26104

type R2 = ReturnType<typeof f>;
function f(): R2 { return 0; }


//// [circularTypeofWithVarOrFunc.js]
var varOfAliasedType1;
var varOfAliasedType2;
function func() { return null; }
var varOfAliasedType3 = func();
function mul(input) {
return input.a * input.b;
}
function f() { return 0; }
43 changes: 43 additions & 0 deletions tests/baselines/reference/circularTypeofWithVarOrFunc.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,46 @@ type typeAlias3 = typeof varOfAliasedType3;
>typeAlias3 : Symbol(typeAlias3, Decl(circularTypeofWithVarOrFunc.ts, 7, 31))
>varOfAliasedType3 : Symbol(varOfAliasedType3, Decl(circularTypeofWithVarOrFunc.ts, 7, 3))

// Repro from #26104

interface Input {
>Input : Symbol(Input, Decl(circularTypeofWithVarOrFunc.ts, 8, 43))

a: number;
>a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))

b: number;
>b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
}

type R = ReturnType<typeof mul>;
>R : Symbol(R, Decl(circularTypeofWithVarOrFunc.ts, 15, 1))
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
>mul : Symbol(mul, Decl(circularTypeofWithVarOrFunc.ts, 17, 32))

function mul(input: Input): R {
>mul : Symbol(mul, Decl(circularTypeofWithVarOrFunc.ts, 17, 32))
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
>Input : Symbol(Input, Decl(circularTypeofWithVarOrFunc.ts, 8, 43))
>R : Symbol(R, Decl(circularTypeofWithVarOrFunc.ts, 15, 1))

return input.a * input.b;
>input.a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
>a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))
>input.b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
>b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
}

// Repro from #26104

type R2 = ReturnType<typeof f>;
>R2 : Symbol(R2, Decl(circularTypeofWithVarOrFunc.ts, 20, 1))
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
>f : Symbol(f, Decl(circularTypeofWithVarOrFunc.ts, 24, 31))

function f(): R2 { return 0; }
>f : Symbol(f, Decl(circularTypeofWithVarOrFunc.ts, 24, 31))
>R2 : Symbol(R2, Decl(circularTypeofWithVarOrFunc.ts, 20, 1))

38 changes: 38 additions & 0 deletions tests/baselines/reference/circularTypeofWithVarOrFunc.types
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,41 @@ type typeAlias3 = typeof varOfAliasedType3;
>typeAlias3 : any
>varOfAliasedType3 : any

// Repro from #26104

interface Input {
a: number;
>a : number

b: number;
>b : number
}

type R = ReturnType<typeof mul>;
>R : any
>mul : (input: Input) => any

function mul(input: Input): R {
>mul : (input: Input) => any
>input : Input

return input.a * input.b;
>input.a * input.b : number
>input.a : number
>input : Input
>a : number
>input.b : number
>input : Input
>b : number
}

// Repro from #26104

type R2 = ReturnType<typeof f>;
>R2 : any
>f : () => any

function f(): R2 { return 0; }
>f : () => any
>0 : 0

Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,49): error TS2577: Return type annotation circularly references itself.
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,58): error TS2304: Cannot find name 'H'.
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,62): error TS2574: A rest element type must be an array type.
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,79): error TS2304: Cannot find name 'R'.


==== tests/cases/compiler/recursiveResolveTypeMembers.ts (3 errors) ====
==== tests/cases/compiler/recursiveResolveTypeMembers.ts (4 errors) ====
// Repro from #25291

type PromisedTuple<L extends any[], U = (...args: L) => void> =
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2577: Return type annotation circularly references itself.
~
!!! error TS2304: Cannot find name 'H'.
~~~~~~~~~~~~~~~~~~~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,11 @@ declare var G: GConstructor;
>G : GConstructor

var obj13: G1 | G2;
>obj13 : G2 | G1
>obj13 : G1 | G2

if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype property.
>obj13 instanceof G : boolean
>obj13 : G2 | G1
>obj13 : G1 | G2
>G : GConstructor

obj13.foo1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,20 @@ type typeAlias2 = typeof varOfAliasedType2;
function func(): typeAlias3 { return null; }
var varOfAliasedType3 = func();
type typeAlias3 = typeof varOfAliasedType3;

// Repro from #26104

interface Input {
a: number;
b: number;
}

type R = ReturnType<typeof mul>;
function mul(input: Input): R {
return input.a * input.b;
}

// Repro from #26104

type R2 = ReturnType<typeof f>;
function f(): R2 { return 0; }