From aa73ed822618d1819cfd37e0bfb1ca7af0933898 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 18 Oct 2017 13:07:54 -0700 Subject: [PATCH] Fix bugs in jsdoc annotation refactor 1. Transform index signatures to TS index signatures. 2. Print object literals on a single line. 3. Only offer the refactor when it could add types. (There must not be a type annotation already, and there must be a JSDoc that applies.) --- .../refactors/annotateWithTypeFromJSDoc.ts | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index 29da66f6974..98131d0623c 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -23,26 +23,30 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { } const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false); - const decl = findAncestor(node, isDeclarationWithType); - if (!decl || decl.type) { - return undefined; - } - const jsdocType = getJSDocType(decl); - const isFunctionWithJSDoc = isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p))); - if (isFunctionWithJSDoc || jsdocType) { + if (hasUsableJSDoc(findAncestor(node, isDeclarationWithType))) { return [{ name: annotateTypeFromJSDoc.name, description: annotateTypeFromJSDoc.description, actions: [ { - description: annotateTypeFromJSDoc.description, - name: actionName - } + description: annotateTypeFromJSDoc.description, + name: actionName + } ] }]; } } + function hasUsableJSDoc(decl: DeclarationWithType): boolean { + if (!decl) { + return false; + } + if (isFunctionLikeDeclaration(decl)) { + return decl.parameters.some(hasUsableJSDoc) || (!decl.type && !!getJSDocReturnType(decl)); + } + return !decl.type && !!getJSDocType(decl); + } + function getEditsForAction(context: RefactorContext, action: string): RefactorEditInfo | undefined { if (actionName !== action) { return Debug.fail(`actionName !== action: ${actionName} !== ${action}`); @@ -169,7 +173,9 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { case SyntaxKind.TypeReference: return transformJSDocTypeReference(node as TypeReferenceNode); default: - return visitEachChild(node, transformJSDocType, /*context*/ undefined) as TypeNode; + const visited = visitEachChild(node, transformJSDocType, /*context*/ undefined) as TypeNode; + setEmitFlags(visited, EmitFlags.SingleLine); + return visited; } } @@ -202,6 +208,9 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { let name = node.typeName; let args = node.typeArguments; if (isIdentifier(node.typeName)) { + if (isJSDocIndexSignature(node)) { + return transformJSDocIndexSignature(node); + } let text = node.typeName.text; switch (node.typeName.text) { case "String": @@ -226,4 +235,26 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { } return createTypeReferenceNode(name, args); } + + function transformJSDocIndexSignature(node: TypeReferenceNode) { + const index = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + node.typeArguments[0].kind === SyntaxKind.NumberKeyword ? "n" : "s", + /*questionToken*/ undefined, + createTypeReferenceNode(node.typeArguments[0].kind === SyntaxKind.NumberKeyword ? "number" : "string", []), + /*initializer*/ undefined); + const indexSignature = createTypeLiteralNode([createIndexSignature(/*decorators*/ undefined, /*modifiers*/ undefined, [index], node.typeArguments[1])]); + setEmitFlags(indexSignature, EmitFlags.SingleLine); + return indexSignature; + } + + function isJSDocIndexSignature(node: TypeReferenceNode | ExpressionWithTypeArguments) { + return isTypeReferenceNode(node) && + isIdentifier(node.typeName) && + node.typeName.escapedText === "Object" && + node.typeArguments && node.typeArguments.length === 2 && + (node.typeArguments[0].kind === SyntaxKind.StringKeyword || node.typeArguments[0].kind === SyntaxKind.NumberKeyword); + } }