Skip to content

Commit 6589e86

Browse files
authored
getJSDocReturnType gets return type from @type tags (#25486)
* get return type from `@type` tags Previously, getJSDocReturnType did not check the `@type` tag for a type node that has a return type. Now it does. * Improve doc comment of getJSDocReturnType * More type predicates in type guards! * Update API baselines with new documentation (?!)
1 parent c228924 commit 6589e86

File tree

7 files changed

+154
-47
lines changed

7 files changed

+154
-47
lines changed

src/compiler/utilities.ts

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5007,14 +5007,27 @@ namespace ts {
50075007
}
50085008

50095009
/**
5010-
* Gets the return type node for the node if provided via JSDoc's return tag.
5010+
* Gets the return type node for the node if provided via JSDoc return tag or type tag.
50115011
*
50125012
* @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function
5013-
* gets the type from inside the braces.
5013+
* gets the type from inside the braces, after the fat arrow, etc.
50145014
*/
50155015
export function getJSDocReturnType(node: Node): TypeNode | undefined {
50165016
const returnTag = getJSDocReturnTag(node);
5017-
return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
5017+
if (returnTag && returnTag.typeExpression) {
5018+
return returnTag.typeExpression.type;
5019+
}
5020+
const typeTag = getJSDocTypeTag(node);
5021+
if (typeTag && typeTag.typeExpression) {
5022+
const type = typeTag.typeExpression.type;
5023+
if (isTypeLiteralNode(type)) {
5024+
const sig = find(type.members, isCallSignatureDeclaration);
5025+
return sig && sig.type;
5026+
}
5027+
if (isFunctionTypeNode(type)) {
5028+
return type.type;
5029+
}
5030+
}
50185031
}
50195032

50205033
/** Get all JSDoc tags related to a node, including those on parent nodes. */
@@ -6572,45 +6585,6 @@ namespace ts {
65726585
return !!(node as HasType).type;
65736586
}
65746587

6575-
/* True if the node could have a type node a `.type` */
6576-
/* @internal */
6577-
export function couldHaveType(node: Node): node is HasType {
6578-
switch (node.kind) {
6579-
case SyntaxKind.Parameter:
6580-
case SyntaxKind.PropertySignature:
6581-
case SyntaxKind.PropertyDeclaration:
6582-
case SyntaxKind.MethodSignature:
6583-
case SyntaxKind.MethodDeclaration:
6584-
case SyntaxKind.Constructor:
6585-
case SyntaxKind.GetAccessor:
6586-
case SyntaxKind.SetAccessor:
6587-
case SyntaxKind.CallSignature:
6588-
case SyntaxKind.ConstructSignature:
6589-
case SyntaxKind.IndexSignature:
6590-
case SyntaxKind.TypePredicate:
6591-
case SyntaxKind.FunctionType:
6592-
case SyntaxKind.ConstructorType:
6593-
case SyntaxKind.ParenthesizedType:
6594-
case SyntaxKind.TypeOperator:
6595-
case SyntaxKind.MappedType:
6596-
case SyntaxKind.TypeAssertionExpression:
6597-
case SyntaxKind.FunctionExpression:
6598-
case SyntaxKind.ArrowFunction:
6599-
case SyntaxKind.AsExpression:
6600-
case SyntaxKind.VariableDeclaration:
6601-
case SyntaxKind.FunctionDeclaration:
6602-
case SyntaxKind.TypeAliasDeclaration:
6603-
case SyntaxKind.JSDocTypeExpression:
6604-
case SyntaxKind.JSDocNullableType:
6605-
case SyntaxKind.JSDocNonNullableType:
6606-
case SyntaxKind.JSDocOptionalType:
6607-
case SyntaxKind.JSDocFunctionType:
6608-
case SyntaxKind.JSDocVariadicType:
6609-
return true;
6610-
}
6611-
return false;
6612-
}
6613-
66146588
/** True if has initializer node attached to it. */
66156589
/* @internal */
66166590
export function hasInitializer(node: Node): node is HasInitializer {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6772,10 +6772,10 @@ declare namespace ts {
67726772
*/
67736773
function getJSDocType(node: Node): TypeNode | undefined;
67746774
/**
6775-
* Gets the return type node for the node if provided via JSDoc's return tag.
6775+
* Gets the return type node for the node if provided via JSDoc return tag or type tag.
67766776
*
67776777
* @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function
6778-
* gets the type from inside the braces.
6778+
* gets the type from inside the braces, after the fat arrow, etc.
67796779
*/
67806780
function getJSDocReturnType(node: Node): TypeNode | undefined;
67816781
/** Get all JSDoc tags related to a node, including those on parent nodes. */
@@ -7072,7 +7072,6 @@ declare namespace ts {
70727072
function hasJSDocNodes(node: Node): node is HasJSDoc;
70737073
/** True if has type node attached to it. */
70747074
function hasType(node: Node): node is HasType;
7075-
function couldHaveType(node: Node): node is HasType;
70767075
/** True if has initializer node attached to it. */
70777076
function hasInitializer(node: Node): node is HasInitializer;
70787077
/** True if has initializer node attached to it. */

tests/baselines/reference/api/typescript.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,10 +3227,10 @@ declare namespace ts {
32273227
*/
32283228
function getJSDocType(node: Node): TypeNode | undefined;
32293229
/**
3230-
* Gets the return type node for the node if provided via JSDoc's return tag.
3230+
* Gets the return type node for the node if provided via JSDoc return tag or type tag.
32313231
*
32323232
* @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function
3233-
* gets the type from inside the braces.
3233+
* gets the type from inside the braces, after the fat arrow, etc.
32343234
*/
32353235
function getJSDocReturnType(node: Node): TypeNode | undefined;
32363236
/** Get all JSDoc tags related to a node, including those on parent nodes. */
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/conformance/jsdoc/test.js(3,17): error TS2322: Type 'number' is not assignable to type 'string'.
2+
tests/cases/conformance/jsdoc/test.js(5,14): error TS2322: Type 'number' is not assignable to type 'string'.
3+
tests/cases/conformance/jsdoc/test.js(7,24): error TS2322: Type 'number' is not assignable to type 'string'.
4+
tests/cases/conformance/jsdoc/test.js(10,17): error TS2322: Type 'number' is not assignable to type 'string'.
5+
tests/cases/conformance/jsdoc/test.js(12,14): error TS2322: Type 'number' is not assignable to type 'string'.
6+
tests/cases/conformance/jsdoc/test.js(14,24): error TS2322: Type 'number' is not assignable to type 'string'.
7+
8+
9+
==== tests/cases/conformance/jsdoc/test.js (6 errors) ====
10+
// all 6 should error on return statement/expression
11+
/** @type {(x: number) => string} */
12+
function h(x) { return x }
13+
~~~~~~~~
14+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
15+
/** @type {(x: number) => string} */
16+
var f = x => x
17+
~
18+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
19+
/** @type {(x: number) => string} */
20+
var g = function (x) { return x }
21+
~~~~~~~~
22+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
23+
24+
/** @type {{ (x: number): string }} */
25+
function i(x) { return x }
26+
~~~~~~~~
27+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
28+
/** @type {{ (x: number): string }} */
29+
var j = x => x
30+
~
31+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
32+
/** @type {{ (x: number): string }} */
33+
var k = function (x) { return x }
34+
~~~~~~~~
35+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
36+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/conformance/jsdoc/test.js ===
2+
// all 6 should error on return statement/expression
3+
/** @type {(x: number) => string} */
4+
function h(x) { return x }
5+
>h : Symbol(h, Decl(test.js, 0, 0))
6+
>x : Symbol(x, Decl(test.js, 2, 11))
7+
>x : Symbol(x, Decl(test.js, 2, 11))
8+
9+
/** @type {(x: number) => string} */
10+
var f = x => x
11+
>f : Symbol(f, Decl(test.js, 4, 3))
12+
>x : Symbol(x, Decl(test.js, 4, 7))
13+
>x : Symbol(x, Decl(test.js, 4, 7))
14+
15+
/** @type {(x: number) => string} */
16+
var g = function (x) { return x }
17+
>g : Symbol(g, Decl(test.js, 6, 3))
18+
>x : Symbol(x, Decl(test.js, 6, 18))
19+
>x : Symbol(x, Decl(test.js, 6, 18))
20+
21+
/** @type {{ (x: number): string }} */
22+
function i(x) { return x }
23+
>i : Symbol(i, Decl(test.js, 6, 33))
24+
>x : Symbol(x, Decl(test.js, 9, 11))
25+
>x : Symbol(x, Decl(test.js, 9, 11))
26+
27+
/** @type {{ (x: number): string }} */
28+
var j = x => x
29+
>j : Symbol(j, Decl(test.js, 11, 3))
30+
>x : Symbol(x, Decl(test.js, 11, 7))
31+
>x : Symbol(x, Decl(test.js, 11, 7))
32+
33+
/** @type {{ (x: number): string }} */
34+
var k = function (x) { return x }
35+
>k : Symbol(k, Decl(test.js, 13, 3))
36+
>x : Symbol(x, Decl(test.js, 13, 18))
37+
>x : Symbol(x, Decl(test.js, 13, 18))
38+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/jsdoc/test.js ===
2+
// all 6 should error on return statement/expression
3+
/** @type {(x: number) => string} */
4+
function h(x) { return x }
5+
>h : (x: number) => string
6+
>x : number
7+
>x : number
8+
9+
/** @type {(x: number) => string} */
10+
var f = x => x
11+
>f : (x: number) => string
12+
>x => x : (x: number) => string
13+
>x : number
14+
>x : number
15+
16+
/** @type {(x: number) => string} */
17+
var g = function (x) { return x }
18+
>g : (x: number) => string
19+
>function (x) { return x } : (x: number) => string
20+
>x : number
21+
>x : number
22+
23+
/** @type {{ (x: number): string }} */
24+
function i(x) { return x }
25+
>i : (x: number) => string
26+
>x : number
27+
>x : number
28+
29+
/** @type {{ (x: number): string }} */
30+
var j = x => x
31+
>j : (x: number) => string
32+
>x => x : (x: number) => string
33+
>x : number
34+
>x : number
35+
36+
/** @type {{ (x: number): string }} */
37+
var k = function (x) { return x }
38+
>k : (x: number) => string
39+
>function (x) { return x } : (x: number) => string
40+
>x : number
41+
>x : number
42+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @checkJs: true
2+
// @allowJs: true
3+
// @noEmit: true
4+
// @Filename: test.js
5+
// all 6 should error on return statement/expression
6+
/** @type {(x: number) => string} */
7+
function h(x) { return x }
8+
/** @type {(x: number) => string} */
9+
var f = x => x
10+
/** @type {(x: number) => string} */
11+
var g = function (x) { return x }
12+
13+
/** @type {{ (x: number): string }} */
14+
function i(x) { return x }
15+
/** @type {{ (x: number): string }} */
16+
var j = x => x
17+
/** @type {{ (x: number): string }} */
18+
var k = function (x) { return x }

0 commit comments

Comments
 (0)