From ed8949e6edd65773a787cf20feace7d64a85406c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 27 Aug 2018 13:16:02 -0700 Subject: [PATCH 1/2] Get [type] parameter types from @type tag Previously only the return type was used in cases like this: ```js /** @type {(param?: T) => T | undefined} */ function g(param) { return param; } ``` Now the type parameters from the type tag are used, and the compiler gets the type of the parameter by using the position in the signature of the type tag. Fixes #25618 --- src/compiler/checker.ts | 6 ++++++ src/compiler/utilities.ts | 17 ++++++++++++++++- .../typeTagWithGenericSignature.symbols | 15 +++++++++++++++ .../reference/typeTagWithGenericSignature.types | 17 +++++++++++++++++ .../jsdoc/typeTagWithGenericSignature.ts | 13 +++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typeTagWithGenericSignature.symbols create mode 100644 tests/baselines/reference/typeTagWithGenericSignature.types create mode 100644 tests/cases/conformance/jsdoc/typeTagWithGenericSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a551108f75ca8..94fc168c7c5fc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4699,6 +4699,12 @@ namespace ts { return getReturnTypeOfSignature(getterSignature); } } + if (isInJavaScriptFile(declaration)) { + const typeTag = getJSDocType(func); + if (typeTag && isFunctionTypeNode(typeTag)) { + return getTypeAtPosition(getSignatureFromDeclaration(typeTag), func.parameters.indexOf(declaration)); + } + } // Use contextual parameter type if one is available const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration); if (type) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 33dfd2617c979..6b7777f52c8b7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5120,7 +5120,22 @@ namespace ts { Debug.assert(node.parent.kind === SyntaxKind.JSDocComment); return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined) as ReadonlyArray; } - return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray); + if (node.typeParameters) { + return node.typeParameters; + } + if (isInJavaScriptFile(node)) { + const decls = getJSDocTypeParameterDeclarations(node); + if (decls.length) { + return decls; + } + const typeTag = getJSDocType(node); + if (typeTag) { + if (isFunctionTypeNode(typeTag) && typeTag.typeParameters) { + return typeTag.typeParameters; + } + } + } + return emptyArray; } export function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined { diff --git a/tests/baselines/reference/typeTagWithGenericSignature.symbols b/tests/baselines/reference/typeTagWithGenericSignature.symbols new file mode 100644 index 0000000000000..c9a699d3b4a6f --- /dev/null +++ b/tests/baselines/reference/typeTagWithGenericSignature.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/jsdoc/bug25618.js === +/** @type {(param?: T) => T | undefined} */ +function typed(param) { +>typed : Symbol(typed, Decl(bug25618.js, 0, 0)) +>param : Symbol(param, Decl(bug25618.js, 1, 15)) + + return param; +>param : Symbol(param, Decl(bug25618.js, 1, 15)) +} + +var n = typed(1); +>n : Symbol(n, Decl(bug25618.js, 5, 3)) +>typed : Symbol(typed, Decl(bug25618.js, 0, 0)) + + diff --git a/tests/baselines/reference/typeTagWithGenericSignature.types b/tests/baselines/reference/typeTagWithGenericSignature.types new file mode 100644 index 0000000000000..585fd49fbf48b --- /dev/null +++ b/tests/baselines/reference/typeTagWithGenericSignature.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/jsdoc/bug25618.js === +/** @type {(param?: T) => T | undefined} */ +function typed(param) { +>typed : (param: T | undefined) => T | undefined +>param : T | undefined + + return param; +>param : T | undefined +} + +var n = typed(1); +>n : number | undefined +>typed(1) : 1 | undefined +>typed : (param: T | undefined) => T | undefined +>1 : 1 + + diff --git a/tests/cases/conformance/jsdoc/typeTagWithGenericSignature.ts b/tests/cases/conformance/jsdoc/typeTagWithGenericSignature.ts new file mode 100644 index 0000000000000..04ef3e5e1a05d --- /dev/null +++ b/tests/cases/conformance/jsdoc/typeTagWithGenericSignature.ts @@ -0,0 +1,13 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true +// @strict: true +// @Filename: bug25618.js + +/** @type {(param?: T) => T | undefined} */ +function typed(param) { + return param; +} + +var n = typed(1); + From 43a2b897b60983b476c8e55bc320acfe1a93b3d3 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 27 Aug 2018 16:32:57 -0700 Subject: [PATCH 2/2] Fix split ifs according to PR comments --- src/compiler/utilities.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6b7777f52c8b7..7f65025d7fb3b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5129,10 +5129,8 @@ namespace ts { return decls; } const typeTag = getJSDocType(node); - if (typeTag) { - if (isFunctionTypeNode(typeTag) && typeTag.typeParameters) { - return typeTag.typeParameters; - } + if (typeTag && isFunctionTypeNode(typeTag) && typeTag.typeParameters) { + return typeTag.typeParameters; } } return emptyArray;