From 6589e867fed572df6d22b6ece99270610266e3f5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 6 Jul 2018 10:46:43 -0700 Subject: [PATCH] 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 (?!) --- src/compiler/utilities.ts | 58 +++++-------------- .../reference/api/tsserverlibrary.d.ts | 5 +- tests/baselines/reference/api/typescript.d.ts | 4 +- .../reference/checkJsdocTypeTag5.errors.txt | 36 ++++++++++++ .../reference/checkJsdocTypeTag5.symbols | 38 ++++++++++++ .../reference/checkJsdocTypeTag5.types | 42 ++++++++++++++ .../conformance/jsdoc/checkJsdocTypeTag5.ts | 18 ++++++ 7 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 tests/baselines/reference/checkJsdocTypeTag5.errors.txt create mode 100644 tests/baselines/reference/checkJsdocTypeTag5.symbols create mode 100644 tests/baselines/reference/checkJsdocTypeTag5.types create mode 100644 tests/cases/conformance/jsdoc/checkJsdocTypeTag5.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3fffc50b56c..6064ef4e6a5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5007,14 +5007,27 @@ namespace ts { } /** - * Gets the return type node for the node if provided via JSDoc's return tag. + * Gets the return type node for the node if provided via JSDoc return tag or type tag. * * @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function - * gets the type from inside the braces. + * gets the type from inside the braces, after the fat arrow, etc. */ export function getJSDocReturnType(node: Node): TypeNode | undefined { const returnTag = getJSDocReturnTag(node); - return returnTag && returnTag.typeExpression && returnTag.typeExpression.type; + if (returnTag && returnTag.typeExpression) { + return returnTag.typeExpression.type; + } + const typeTag = getJSDocTypeTag(node); + if (typeTag && typeTag.typeExpression) { + const type = typeTag.typeExpression.type; + if (isTypeLiteralNode(type)) { + const sig = find(type.members, isCallSignatureDeclaration); + return sig && sig.type; + } + if (isFunctionTypeNode(type)) { + return type.type; + } + } } /** Get all JSDoc tags related to a node, including those on parent nodes. */ @@ -6572,45 +6585,6 @@ namespace ts { return !!(node as HasType).type; } - /* True if the node could have a type node a `.type` */ - /* @internal */ - export function couldHaveType(node: Node): node is HasType { - switch (node.kind) { - case SyntaxKind.Parameter: - case SyntaxKind.PropertySignature: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.TypePredicate: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.ParenthesizedType: - case SyntaxKind.TypeOperator: - case SyntaxKind.MappedType: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.AsExpression: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.JSDocTypeExpression: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocOptionalType: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.JSDocVariadicType: - return true; - } - return false; - } - /** True if has initializer node attached to it. */ /* @internal */ export function hasInitializer(node: Node): node is HasInitializer { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 7d1a8d4af81..bc279b685d3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -6772,10 +6772,10 @@ declare namespace ts { */ function getJSDocType(node: Node): TypeNode | undefined; /** - * Gets the return type node for the node if provided via JSDoc's return tag. + * Gets the return type node for the node if provided via JSDoc return tag or type tag. * * @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function - * gets the type from inside the braces. + * gets the type from inside the braces, after the fat arrow, etc. */ function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ @@ -7072,7 +7072,6 @@ declare namespace ts { function hasJSDocNodes(node: Node): node is HasJSDoc; /** True if has type node attached to it. */ function hasType(node: Node): node is HasType; - function couldHaveType(node: Node): node is HasType; /** True if has initializer node attached to it. */ function hasInitializer(node: Node): node is HasInitializer; /** True if has initializer node attached to it. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index dfe6093f6ac..ce835597cd9 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3227,10 +3227,10 @@ declare namespace ts { */ function getJSDocType(node: Node): TypeNode | undefined; /** - * Gets the return type node for the node if provided via JSDoc's return tag. + * Gets the return type node for the node if provided via JSDoc return tag or type tag. * * @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function - * gets the type from inside the braces. + * gets the type from inside the braces, after the fat arrow, etc. */ function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ diff --git a/tests/baselines/reference/checkJsdocTypeTag5.errors.txt b/tests/baselines/reference/checkJsdocTypeTag5.errors.txt new file mode 100644 index 00000000000..98510f63815 --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypeTag5.errors.txt @@ -0,0 +1,36 @@ +tests/cases/conformance/jsdoc/test.js(3,17): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/test.js(5,14): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/test.js(7,24): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/test.js(10,17): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/test.js(12,14): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/test.js(14,24): error TS2322: Type 'number' is not assignable to type 'string'. + + +==== tests/cases/conformance/jsdoc/test.js (6 errors) ==== + // all 6 should error on return statement/expression + /** @type {(x: number) => string} */ + function h(x) { return x } + ~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {(x: number) => string} */ + var f = x => x + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {(x: number) => string} */ + var g = function (x) { return x } + ~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + /** @type {{ (x: number): string }} */ + function i(x) { return x } + ~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {{ (x: number): string }} */ + var j = x => x + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {{ (x: number): string }} */ + var k = function (x) { return x } + ~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocTypeTag5.symbols b/tests/baselines/reference/checkJsdocTypeTag5.symbols new file mode 100644 index 00000000000..839fba179ac --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypeTag5.symbols @@ -0,0 +1,38 @@ +=== tests/cases/conformance/jsdoc/test.js === +// all 6 should error on return statement/expression +/** @type {(x: number) => string} */ +function h(x) { return x } +>h : Symbol(h, Decl(test.js, 0, 0)) +>x : Symbol(x, Decl(test.js, 2, 11)) +>x : Symbol(x, Decl(test.js, 2, 11)) + +/** @type {(x: number) => string} */ +var f = x => x +>f : Symbol(f, Decl(test.js, 4, 3)) +>x : Symbol(x, Decl(test.js, 4, 7)) +>x : Symbol(x, Decl(test.js, 4, 7)) + +/** @type {(x: number) => string} */ +var g = function (x) { return x } +>g : Symbol(g, Decl(test.js, 6, 3)) +>x : Symbol(x, Decl(test.js, 6, 18)) +>x : Symbol(x, Decl(test.js, 6, 18)) + +/** @type {{ (x: number): string }} */ +function i(x) { return x } +>i : Symbol(i, Decl(test.js, 6, 33)) +>x : Symbol(x, Decl(test.js, 9, 11)) +>x : Symbol(x, Decl(test.js, 9, 11)) + +/** @type {{ (x: number): string }} */ +var j = x => x +>j : Symbol(j, Decl(test.js, 11, 3)) +>x : Symbol(x, Decl(test.js, 11, 7)) +>x : Symbol(x, Decl(test.js, 11, 7)) + +/** @type {{ (x: number): string }} */ +var k = function (x) { return x } +>k : Symbol(k, Decl(test.js, 13, 3)) +>x : Symbol(x, Decl(test.js, 13, 18)) +>x : Symbol(x, Decl(test.js, 13, 18)) + diff --git a/tests/baselines/reference/checkJsdocTypeTag5.types b/tests/baselines/reference/checkJsdocTypeTag5.types new file mode 100644 index 00000000000..1038fe0ff3c --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypeTag5.types @@ -0,0 +1,42 @@ +=== tests/cases/conformance/jsdoc/test.js === +// all 6 should error on return statement/expression +/** @type {(x: number) => string} */ +function h(x) { return x } +>h : (x: number) => string +>x : number +>x : number + +/** @type {(x: number) => string} */ +var f = x => x +>f : (x: number) => string +>x => x : (x: number) => string +>x : number +>x : number + +/** @type {(x: number) => string} */ +var g = function (x) { return x } +>g : (x: number) => string +>function (x) { return x } : (x: number) => string +>x : number +>x : number + +/** @type {{ (x: number): string }} */ +function i(x) { return x } +>i : (x: number) => string +>x : number +>x : number + +/** @type {{ (x: number): string }} */ +var j = x => x +>j : (x: number) => string +>x => x : (x: number) => string +>x : number +>x : number + +/** @type {{ (x: number): string }} */ +var k = function (x) { return x } +>k : (x: number) => string +>function (x) { return x } : (x: number) => string +>x : number +>x : number + diff --git a/tests/cases/conformance/jsdoc/checkJsdocTypeTag5.ts b/tests/cases/conformance/jsdoc/checkJsdocTypeTag5.ts new file mode 100644 index 00000000000..f56b99b9e99 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkJsdocTypeTag5.ts @@ -0,0 +1,18 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true +// @Filename: test.js +// all 6 should error on return statement/expression +/** @type {(x: number) => string} */ +function h(x) { return x } +/** @type {(x: number) => string} */ +var f = x => x +/** @type {(x: number) => string} */ +var g = function (x) { return x } + +/** @type {{ (x: number): string }} */ +function i(x) { return x } +/** @type {{ (x: number): string }} */ +var j = x => x +/** @type {{ (x: number): string }} */ +var k = function (x) { return x }