From 123347d5c42390068325590f348b4e7be3b78f87 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 12 Oct 2017 11:40:07 -0700 Subject: [PATCH] Convert @template tag to type parameters in refactor --- src/compiler/utilities.ts | 16 ++++++------ .../refactors/annotateWithTypeFromJSDoc.ts | 25 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2c1e25f74af..c4d3de453ba 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2700,11 +2700,11 @@ namespace ts { * Gets the effective type annotation of a variable, parameter, or property. If the node was * parsed in a JavaScript file, gets the type annotation from JSDoc. */ - export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode | undefined { + export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration, checkJSDoc?: boolean): TypeNode | undefined { if (node.type) { return node.type; } - if (isInJavaScriptFile(node)) { + if (checkJSDoc || isInJavaScriptFile(node)) { return getJSDocType(node); } } @@ -2713,11 +2713,11 @@ namespace ts { * Gets the effective return type annotation of a signature. If the node was parsed in a * JavaScript file, gets the return type annotation from JSDoc. */ - export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode | undefined { + export function getEffectiveReturnTypeNode(node: SignatureDeclaration, checkJSDoc?: boolean): TypeNode | undefined { if (node.type) { return node.type; } - if (isInJavaScriptFile(node)) { + if (checkJSDoc || isInJavaScriptFile(node)) { return getJSDocReturnType(node); } } @@ -2726,11 +2726,11 @@ namespace ts { * Gets the effective type parameters. If the node was parsed in a * JavaScript file, gets the type parameters from the `@template` tag from JSDoc. */ - export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray { + export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters, checkJSDoc?: boolean): ReadonlyArray { if (node.typeParameters) { return node.typeParameters; } - if (isInJavaScriptFile(node)) { + if (checkJSDoc || isInJavaScriptFile(node)) { const templateTag = getJSDocTemplateTag(node); return templateTag && templateTag.typeParameters; } @@ -2740,9 +2740,9 @@ namespace ts { * Gets the effective type annotation of the value parameter of a set accessor. If the node * was parsed in a JavaScript file, gets the type annotation from JSDoc. */ - export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode { + export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration, checkJSDoc?: boolean): TypeNode { const parameter = getSetAccessorValueParameter(node); - return parameter && getEffectiveTypeAnnotationNode(parameter); + return parameter && getEffectiveTypeAnnotationNode(parameter, checkJSDoc); } export function emitNewLineBeforeLeadingComments(lineMap: ReadonlyArray, writer: EmitTextWriter, node: TextRange, leadingComments: ReadonlyArray) { diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index 529793b7cd1..bc6dcd073bf 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -31,11 +31,11 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { } const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false); - const decl = findAncestor(node, isTypedNode); + const decl = findAncestor(node, isDeclarationWithType); if (decl && !decl.type) { const type = getJSDocType(decl); - const returnType = getJSDocReturnType(decl); - const annotate = (returnType || type && decl.kind === SyntaxKind.Parameter) ? annotateFunctionFromJSDoc : + const isFunctionWithJSDoc = isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p))); + const annotate = (isFunctionWithJSDoc || type && decl.kind === SyntaxKind.Parameter) ? annotateFunctionFromJSDoc : type ? annotateTypeFromJSDoc : undefined; if (annotate) { @@ -61,7 +61,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { const sourceFile = context.file; const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); - const decl = findAncestor(token, isTypedNode); + const decl = findAncestor(token, isDeclarationWithType); const jsdocType = getJSDocReturnType(decl) || getJSDocType(decl); if (!decl || !jsdocType || decl.type) { Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`); @@ -95,7 +95,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { }; } - function isTypedNode(node: Node): node is DeclarationWithType { + function isDeclarationWithType(node: Node): node is DeclarationWithType { return isFunctionLikeDeclaration(node) || node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.Parameter || @@ -104,22 +104,23 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { } function addTypesToFunctionLike(decl: FunctionLikeDeclaration) { - const returnType = decl.type || transformJSDocType(getJSDocReturnType(decl)) as TypeNode; + const typeParameters = getEffectiveTypeParameterDeclarations(decl, /*checkJSDoc*/ true); const parameters = decl.parameters.map( - p => createParameter(p.decorators, p.modifiers, p.dotDotDotToken, p.name, p.questionToken, p.type || transformJSDocType(getJSDocType(p)) as TypeNode, p.initializer)); + p => createParameter(p.decorators, p.modifiers, p.dotDotDotToken, p.name, p.questionToken, transformJSDocType(getEffectiveTypeAnnotationNode(p, /*checkJSDoc*/ true)) as TypeNode, p.initializer)); + const returnType = transformJSDocType(getEffectiveReturnTypeNode(decl, /*checkJSDoc*/ true)) as TypeNode; switch (decl.kind) { case SyntaxKind.FunctionDeclaration: - return createFunctionDeclaration(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.typeParameters, parameters, returnType, decl.body); + return createFunctionDeclaration(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, typeParameters, parameters, returnType, decl.body); case SyntaxKind.Constructor: return createConstructor(decl.decorators, decl.modifiers, parameters, decl.body); case SyntaxKind.FunctionExpression: - return createFunctionExpression(decl.modifiers, decl.asteriskToken, (decl as FunctionExpression).name, decl.typeParameters, parameters, returnType, decl.body); + return createFunctionExpression(decl.modifiers, decl.asteriskToken, (decl as FunctionExpression).name, typeParameters, parameters, returnType, decl.body); case SyntaxKind.ArrowFunction: - return createArrowFunction(decl.modifiers, decl.typeParameters, parameters, returnType, decl.equalsGreaterThanToken, decl.body); + return createArrowFunction(decl.modifiers, typeParameters, parameters, returnType, decl.equalsGreaterThanToken, decl.body); case SyntaxKind.MethodDeclaration: - return createMethod(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.questionToken, decl.typeParameters, parameters, returnType, decl.body); + return createMethod(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.questionToken, typeParameters, parameters, returnType, decl.body); case SyntaxKind.GetAccessor: - return createGetAccessor(decl.decorators, decl.modifiers, decl.name, parameters, returnType, decl.body); + return createGetAccessor(decl.decorators, decl.modifiers, decl.name, decl.parameters, returnType, decl.body); case SyntaxKind.SetAccessor: return createSetAccessor(decl.decorators, decl.modifiers, decl.name, parameters, decl.body); default: