Skip to content

Commit eeb19c1

Browse files
authored
Merge pull request #26236 from Microsoft/fixCircularReturnType
Fix circular return type issue
2 parents 2e017b8 + cfa29ae commit eeb19c1

13 files changed

+269
-113
lines changed

src/compiler/checker.ts

+87-97
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,10 @@
20682068
"category": "Error",
20692069
"code": 2576
20702070
},
2071+
"Return type annotation circularly references itself.": {
2072+
"category": "Error",
2073+
"code": 2577
2074+
},
20712075
"JSX element attributes type '{0}' may not be a union type.": {
20722076
"category": "Error",
20732077
"code": 2600

src/compiler/utilities.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -3374,10 +3374,9 @@ namespace ts {
33743374
* JavaScript file, gets the return type annotation from JSDoc.
33753375
*/
33763376
export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
3377-
if (isJSDocSignature(node)) {
3378-
return node.type && node.type.typeExpression && node.type.typeExpression.type;
3379-
}
3380-
return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
3377+
return isJSDocSignature(node) ?
3378+
node.type && node.type.typeExpression && node.type.typeExpression.type :
3379+
node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
33813380
}
33823381

33833382
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {

tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
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.
2+
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.
23
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.
34
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.
5+
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.
46
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.
5-
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.
67
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.
78
Type 'Thenable' is not assignable to type 'PromiseLike<T>'.
89
Types of property 'then' are incompatible.
@@ -12,7 +13,7 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
1213
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.
1314

1415

15-
==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (8 errors) ====
16+
==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (9 errors) ====
1617
declare class Thenable { then(): void; }
1718
declare let a: any;
1819
declare let obj: { then: string; };
@@ -21,19 +22,21 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
2122
async function fn2(): { } { } // error
2223
~~~
2324
!!! 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.
25+
~~~
26+
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
2427
async function fn3(): any { } // error
2528
~~~
2629
!!! 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.
2730
async function fn4(): number { } // error
2831
~~~~~~
2932
!!! 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.
33+
~~~~~~
34+
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
3035
async function fn5(): PromiseLike<void> { } // error
3136
~~~~~~~~~~~~~~~~~
3237
!!! 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.
3338
async function fn6(): Thenable { } // error
3439
~~~~~~~~
35-
!!! 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.
36-
~~~~~~~~
3740
!!! 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.
3841
!!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike<T>'.
3942
!!! error TS1055: Types of property 'then' are incompatible.

tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
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.
2+
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.
23
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.
34
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.
5+
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.
46
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.
57
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.
68
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.
79
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.
810

911

10-
==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ====
12+
==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (9 errors) ====
1113
declare class Thenable { then(): void; }
1214
declare let a: any;
1315
declare let obj: { then: string; };
@@ -16,12 +18,16 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
1618
async function fn2(): { } { } // error
1719
~~~
1820
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
21+
~~~
22+
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
1923
async function fn3(): any { } // error
2024
~~~
2125
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
2226
async function fn4(): number { } // error
2327
~~~~~~
2428
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.
29+
~~~~~~
30+
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
2531
async function fn5(): PromiseLike<void> { } // error
2632
~~~~~~~~~~~~~~~~~
2733
!!! error TS1064: The return type of an async function or method must be the global Promise<T> type.

tests/baselines/reference/checkJsdocTypeTag6.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ var g = function (prop) {
1616

1717
/** @type {(a: number) => number} */
1818
function add1(a, b) { return a + b; }
19-
>add1 : (a: any, b: any) => number
20-
>a : any
19+
>add1 : (a: number, b: any) => number
20+
>a : number
2121
>b : any
2222
>a + b : any
23-
>a : any
23+
>a : number
2424
>b : any
2525

2626
/** @type {(a: number, b: number) => number} */

tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt

+33-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarO
22
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(2,5): error TS2502: 'varOfAliasedType1' is referenced directly or indirectly in its own type annotation.
33
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(4,5): error TS2502: 'varOfAliasedType2' is referenced directly or indirectly in its own type annotation.
44
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(5,6): error TS2456: Type alias 'typeAlias2' circularly references itself.
5+
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(7,18): error TS2577: Return type annotation circularly references itself.
56
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(9,6): error TS2456: Type alias 'typeAlias3' circularly references itself.
7+
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(18,6): error TS2456: Type alias 'R' circularly references itself.
8+
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(19,29): error TS2577: Return type annotation circularly references itself.
9+
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(25,6): error TS2456: Type alias 'R2' circularly references itself.
10+
tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(26,15): error TS2577: Return type annotation circularly references itself.
611

712

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

2328
function func(): typeAlias3 { return null; }
29+
~~~~~~~~~~
30+
!!! error TS2577: Return type annotation circularly references itself.
2431
var varOfAliasedType3 = func();
2532
type typeAlias3 = typeof varOfAliasedType3;
2633
~~~~~~~~~~
2734
!!! error TS2456: Type alias 'typeAlias3' circularly references itself.
35+
36+
// Repro from #26104
37+
38+
interface Input {
39+
a: number;
40+
b: number;
41+
}
42+
43+
type R = ReturnType<typeof mul>;
44+
~
45+
!!! error TS2456: Type alias 'R' circularly references itself.
46+
function mul(input: Input): R {
47+
~
48+
!!! error TS2577: Return type annotation circularly references itself.
49+
return input.a * input.b;
50+
}
51+
52+
// Repro from #26104
53+
54+
type R2 = ReturnType<typeof f>;
55+
~~
56+
!!! error TS2456: Type alias 'R2' circularly references itself.
57+
function f(): R2 { return 0; }
58+
~~
59+
!!! error TS2577: Return type annotation circularly references itself.
2860

tests/baselines/reference/circularTypeofWithVarOrFunc.js

+21
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,31 @@ type typeAlias2 = typeof varOfAliasedType2;
88
function func(): typeAlias3 { return null; }
99
var varOfAliasedType3 = func();
1010
type typeAlias3 = typeof varOfAliasedType3;
11+
12+
// Repro from #26104
13+
14+
interface Input {
15+
a: number;
16+
b: number;
17+
}
18+
19+
type R = ReturnType<typeof mul>;
20+
function mul(input: Input): R {
21+
return input.a * input.b;
22+
}
23+
24+
// Repro from #26104
25+
26+
type R2 = ReturnType<typeof f>;
27+
function f(): R2 { return 0; }
1128

1229

1330
//// [circularTypeofWithVarOrFunc.js]
1431
var varOfAliasedType1;
1532
var varOfAliasedType2;
1633
function func() { return null; }
1734
var varOfAliasedType3 = func();
35+
function mul(input) {
36+
return input.a * input.b;
37+
}
38+
function f() { return 0; }

tests/baselines/reference/circularTypeofWithVarOrFunc.symbols

+43
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,46 @@ type typeAlias3 = typeof varOfAliasedType3;
2727
>typeAlias3 : Symbol(typeAlias3, Decl(circularTypeofWithVarOrFunc.ts, 7, 31))
2828
>varOfAliasedType3 : Symbol(varOfAliasedType3, Decl(circularTypeofWithVarOrFunc.ts, 7, 3))
2929

30+
// Repro from #26104
31+
32+
interface Input {
33+
>Input : Symbol(Input, Decl(circularTypeofWithVarOrFunc.ts, 8, 43))
34+
35+
a: number;
36+
>a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))
37+
38+
b: number;
39+
>b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
40+
}
41+
42+
type R = ReturnType<typeof mul>;
43+
>R : Symbol(R, Decl(circularTypeofWithVarOrFunc.ts, 15, 1))
44+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
45+
>mul : Symbol(mul, Decl(circularTypeofWithVarOrFunc.ts, 17, 32))
46+
47+
function mul(input: Input): R {
48+
>mul : Symbol(mul, Decl(circularTypeofWithVarOrFunc.ts, 17, 32))
49+
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
50+
>Input : Symbol(Input, Decl(circularTypeofWithVarOrFunc.ts, 8, 43))
51+
>R : Symbol(R, Decl(circularTypeofWithVarOrFunc.ts, 15, 1))
52+
53+
return input.a * input.b;
54+
>input.a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))
55+
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
56+
>a : Symbol(Input.a, Decl(circularTypeofWithVarOrFunc.ts, 12, 17))
57+
>input.b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
58+
>input : Symbol(input, Decl(circularTypeofWithVarOrFunc.ts, 18, 13))
59+
>b : Symbol(Input.b, Decl(circularTypeofWithVarOrFunc.ts, 13, 12))
60+
}
61+
62+
// Repro from #26104
63+
64+
type R2 = ReturnType<typeof f>;
65+
>R2 : Symbol(R2, Decl(circularTypeofWithVarOrFunc.ts, 20, 1))
66+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
67+
>f : Symbol(f, Decl(circularTypeofWithVarOrFunc.ts, 24, 31))
68+
69+
function f(): R2 { return 0; }
70+
>f : Symbol(f, Decl(circularTypeofWithVarOrFunc.ts, 24, 31))
71+
>R2 : Symbol(R2, Decl(circularTypeofWithVarOrFunc.ts, 20, 1))
72+

tests/baselines/reference/circularTypeofWithVarOrFunc.types

+38
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,41 @@ type typeAlias3 = typeof varOfAliasedType3;
2626
>typeAlias3 : any
2727
>varOfAliasedType3 : any
2828

29+
// Repro from #26104
30+
31+
interface Input {
32+
a: number;
33+
>a : number
34+
35+
b: number;
36+
>b : number
37+
}
38+
39+
type R = ReturnType<typeof mul>;
40+
>R : any
41+
>mul : (input: Input) => any
42+
43+
function mul(input: Input): R {
44+
>mul : (input: Input) => any
45+
>input : Input
46+
47+
return input.a * input.b;
48+
>input.a * input.b : number
49+
>input.a : number
50+
>input : Input
51+
>a : number
52+
>input.b : number
53+
>input : Input
54+
>b : number
55+
}
56+
57+
// Repro from #26104
58+
59+
type R2 = ReturnType<typeof f>;
60+
>R2 : any
61+
>f : () => any
62+
63+
function f(): R2 { return 0; }
64+
>f : () => any
65+
>0 : 0
66+

tests/baselines/reference/recursiveResolveTypeMembers.errors.txt

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

56

6-
==== tests/cases/compiler/recursiveResolveTypeMembers.ts (3 errors) ====
7+
==== tests/cases/compiler/recursiveResolveTypeMembers.ts (4 errors) ====
78
// Repro from #25291
89

910
type PromisedTuple<L extends any[], U = (...args: L) => void> =
1011
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
12+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
!!! error TS2577: Return type annotation circularly references itself.
1114
~
1215
!!! error TS2304: Cannot find name 'H'.
1316
~~~~~~~~~~~~~~~~~~~

tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -388,11 +388,11 @@ declare var G: GConstructor;
388388
>G : GConstructor
389389

390390
var obj13: G1 | G2;
391-
>obj13 : G2 | G1
391+
>obj13 : G1 | G2
392392

393393
if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype property.
394394
>obj13 instanceof G : boolean
395-
>obj13 : G2 | G1
395+
>obj13 : G1 | G2
396396
>G : GConstructor
397397

398398
obj13.foo1;

tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts

+17
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,20 @@ type typeAlias2 = typeof varOfAliasedType2;
77
function func(): typeAlias3 { return null; }
88
var varOfAliasedType3 = func();
99
type typeAlias3 = typeof varOfAliasedType3;
10+
11+
// Repro from #26104
12+
13+
interface Input {
14+
a: number;
15+
b: number;
16+
}
17+
18+
type R = ReturnType<typeof mul>;
19+
function mul(input: Input): R {
20+
return input.a * input.b;
21+
}
22+
23+
// Repro from #26104
24+
25+
type R2 = ReturnType<typeof f>;
26+
function f(): R2 { return 0; }

0 commit comments

Comments
 (0)