From 4fab415a92b3ccc37f49e3116c7bc44135a0bae2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 6 Jul 2018 10:12:46 -0700 Subject: [PATCH 1/4] 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. --- src/compiler/utilities.ts | 54 +++++-------------- .../reference/api/tsserverlibrary.d.ts | 1 - .../reference/checkJsdocTypeTag5.errors.txt | 36 +++++++++++++ .../reference/checkJsdocTypeTag5.symbols | 38 +++++++++++++ .../reference/checkJsdocTypeTag5.types | 42 +++++++++++++++ .../conformance/jsdoc/checkJsdocTypeTag5.ts | 18 +++++++ 6 files changed, 148 insertions(+), 41 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 c5fa7d155d519..2d6062d5826a5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5029,7 +5029,20 @@ namespace ts { */ 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 (type.kind === SyntaxKind.TypeLiteral) { + const sig = find((type as TypeLiteralNode).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 82b7e0c3ef833..1b20bd896b292 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -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/checkJsdocTypeTag5.errors.txt b/tests/baselines/reference/checkJsdocTypeTag5.errors.txt new file mode 100644 index 0000000000000..98510f63815be --- /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 0000000000000..839fba179ac1f --- /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 0000000000000..1038fe0ff3c74 --- /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 0000000000000..f56b99b9e99e9 --- /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 } From c7d422d8a91b196058e66b161aae0db49021d965 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 6 Jul 2018 10:15:53 -0700 Subject: [PATCH 2/4] Improve doc comment of getJSDocReturnType --- src/compiler/utilities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2d6062d5826a5..0b24d4134e07e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5022,10 +5022,10 @@ 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); From 59e2e4103e242cfc7b986fb2a6dbcfa97a55485e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 6 Jul 2018 10:22:23 -0700 Subject: [PATCH 3/4] More type predicates in type guards! --- src/compiler/utilities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0b24d4134e07e..d98445c4316b4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5035,8 +5035,8 @@ namespace ts { const typeTag = getJSDocTypeTag(node); if (typeTag && typeTag.typeExpression) { const type = typeTag.typeExpression.type; - if (type.kind === SyntaxKind.TypeLiteral) { - const sig = find((type as TypeLiteralNode).members, isCallSignatureDeclaration); + if (isTypeLiteralNode(type)) { + const sig = find(type.members, isCallSignatureDeclaration); return sig && sig.type; } if (isFunctionTypeNode(type)) { From f21f6988cb8850a9b5b199b02aa1c4a1041c1a72 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 6 Jul 2018 10:25:12 -0700 Subject: [PATCH 4/4] Update API baselines with new documentation (?!) --- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 1b20bd896b292..06650d20c71b0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -6777,10 +6777,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/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f20c70e009586..e036deb703b1f 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. */