diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1db368bb54186..8cd5088049c65 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3758,12 +3758,20 @@ "category": "Message", "code": 95008 }, - "Infer type of '{0}' from usage.": { + "Annotate with type from JSDoc": { "category": "Message", "code": 95009 }, - "Infer parameter types from usage.": { + "Annotate with types from JSDoc": { "category": "Message", "code": 95010 + }, + "Infer type of '{0}' from usage.": { + "category": "Message", + "code": 95011 + }, + "Infer parameter types from usage.": { + "category": "Message", + "code": 95012 } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index da6c0e0fca8f6..9ce220e723f92 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -546,6 +546,8 @@ namespace ts { return emitTypeReference(node); case SyntaxKind.FunctionType: return emitFunctionType(node); + case SyntaxKind.JSDocFunctionType: + return emitJSDocFunctionType(node as JSDocFunctionType); case SyntaxKind.ConstructorType: return emitConstructorType(node); case SyntaxKind.TypeQuery: @@ -574,6 +576,20 @@ namespace ts { return emitMappedType(node); case SyntaxKind.LiteralType: return emitLiteralType(node); + case SyntaxKind.JSDocAllType: + write("*"); + return; + case SyntaxKind.JSDocUnknownType: + write("?"); + return; + case SyntaxKind.JSDocNullableType: + return emitJSDocNullableType(node as JSDocNullableType); + case SyntaxKind.JSDocNonNullableType: + return emitJSDocNonNullableType(node as JSDocNonNullableType); + case SyntaxKind.JSDocOptionalType: + return emitJSDocOptionalType(node as JSDocOptionalType); + case SyntaxKind.JSDocVariadicType: + return emitJSDocVariadicType(node as JSDocVariadicType); // Binding patterns case SyntaxKind.ObjectBindingPattern: @@ -914,9 +930,16 @@ namespace ts { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitIfPresent(node.dotDotDotToken); - emit(node.name); + if (node.name) { + emit(node.name); + } emitIfPresent(node.questionToken); - emitWithPrefix(": ", node.type); + if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) { + emit(node.type); + } + else { + emitWithPrefix(": ", node.type); + } emitExpressionWithPrefix(" = ", node.initializer); } @@ -1035,6 +1058,29 @@ namespace ts { emit(node.type); } + function emitJSDocFunctionType(node: JSDocFunctionType) { + write("function"); + emitParameters(node, node.parameters); + write(":"); + emit(node.type); + } + + + function emitJSDocNullableType(node: JSDocNullableType) { + write("?"); + emit(node.type); + } + + function emitJSDocNonNullableType(node: JSDocNonNullableType) { + write("!"); + emit(node.type); + } + + function emitJSDocOptionalType(node: JSDocOptionalType) { + emit(node.type); + write("="); + } + function emitConstructorType(node: ConstructorTypeNode) { write("new "); emitTypeParameters(node, node.typeParameters); @@ -1060,6 +1106,11 @@ namespace ts { write("[]"); } + function emitJSDocVariadicType(node: JSDocVariadicType) { + write("..."); + emit(node.type); + } + function emitTupleType(node: TupleTypeNode) { write("["); emitList(node, node.elementTypes, ListFormat.TupleTypeElements); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4688a7779e383..c4d3de453ba5c 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) { @@ -5130,7 +5130,14 @@ namespace ts { || kind === SyntaxKind.UndefinedKeyword || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NeverKeyword - || kind === SyntaxKind.ExpressionWithTypeArguments; + || kind === SyntaxKind.ExpressionWithTypeArguments + || kind === SyntaxKind.JSDocAllType + || kind === SyntaxKind.JSDocUnknownType + || kind === SyntaxKind.JSDocNullableType + || kind === SyntaxKind.JSDocNonNullableType + || kind === SyntaxKind.JSDocOptionalType + || kind === SyntaxKind.JSDocFunctionType + || kind === SyntaxKind.JSDocVariadicType; } /** diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts new file mode 100644 index 0000000000000..60c0517f68113 --- /dev/null +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -0,0 +1,226 @@ +/* @internal */ +namespace ts.refactor.annotateWithTypeFromJSDoc { + const actionName = "annotate"; + + const annotateTypeFromJSDoc: Refactor = { + name: "Annotate with type from JSDoc", + description: Diagnostics.Annotate_with_type_from_JSDoc.message, + getEditsForAction: getEditsForAnnotation, + getAvailableActions + }; + const annotateFunctionFromJSDoc: Refactor = { + name: "Annotate with types from JSDoc", + description: Diagnostics.Annotate_with_types_from_JSDoc.message, + getEditsForAction: getEditsForFunctionAnnotation, + getAvailableActions + }; + + type DeclarationWithType = + | FunctionLikeDeclaration + | VariableDeclaration + | ParameterDeclaration + | PropertySignature + | PropertyDeclaration; + + registerRefactor(annotateTypeFromJSDoc); + registerRefactor(annotateFunctionFromJSDoc); + + function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { + if (isInJavaScriptFile(context.file)) { + return undefined; + } + + 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))); + const refactor = (isFunctionWithJSDoc || jsdocType && decl.kind === SyntaxKind.Parameter) ? annotateFunctionFromJSDoc : + jsdocType ? annotateTypeFromJSDoc : + undefined; + if (refactor) { + return [{ + name: refactor.name, + description: refactor.description, + actions: [ + { + description: refactor.description, + name: actionName + } + ] + }]; + } + } + + function getEditsForAnnotation(context: RefactorContext, action: string): RefactorEditInfo | undefined { + if (actionName !== action) { + return Debug.fail(`actionName !== action: ${actionName} !== ${action}`); + } + + const sourceFile = context.file; + const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); + const decl = findAncestor(token, isDeclarationWithType); + const jsdocType = getJSDocType(decl); + if (!decl || !jsdocType || decl.type) { + return Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`); + } + + const changeTracker = textChanges.ChangeTracker.fromContext(context); + const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); + suppressLeadingAndTrailingTrivia(declarationWithType); + changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType); + return { + edits: changeTracker.getChanges(), + renameFilename: undefined, + renameLocation: undefined + }; + } + + function getEditsForFunctionAnnotation(context: RefactorContext, action: string): RefactorEditInfo | undefined { + if (actionName !== action) { + return Debug.fail(`actionName !== action: ${actionName} !== ${action}`); + } + + const sourceFile = context.file; + const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); + const decl = findAncestor(token, isFunctionLikeDeclaration); + const changeTracker = textChanges.ChangeTracker.fromContext(context); + const functionWithType = addTypesToFunctionLike(decl); + suppressLeadingAndTrailingTrivia(functionWithType); + changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType); + return { + edits: changeTracker.getChanges(), + renameFilename: undefined, + renameLocation: undefined + }; + } + + function isDeclarationWithType(node: Node): node is DeclarationWithType { + return isFunctionLikeDeclaration(node) || + node.kind === SyntaxKind.VariableDeclaration || + node.kind === SyntaxKind.Parameter || + node.kind === SyntaxKind.PropertySignature || + node.kind === SyntaxKind.PropertyDeclaration; + } + + function addTypesToFunctionLike(decl: FunctionLikeDeclaration) { + const typeParameters = getEffectiveTypeParameterDeclarations(decl, /*checkJSDoc*/ true); + const parameters = decl.parameters.map( + 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, 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, typeParameters, parameters, returnType, decl.body); + case SyntaxKind.ArrowFunction: + 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, typeParameters, parameters, returnType, decl.body); + case SyntaxKind.GetAccessor: + 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: + return Debug.assertNever(decl, `Unexpected SyntaxKind: ${(decl as any).kind}`); + } + } + + function addType(decl: DeclarationWithType, jsdocType: TypeNode) { + switch (decl.kind) { + case SyntaxKind.VariableDeclaration: + return createVariableDeclaration(decl.name, jsdocType, decl.initializer); + case SyntaxKind.PropertySignature: + return createPropertySignature(decl.modifiers, decl.name, decl.questionToken, jsdocType, decl.initializer); + case SyntaxKind.PropertyDeclaration: + return createProperty(decl.decorators, decl.modifiers, decl.name, decl.questionToken, jsdocType, decl.initializer); + default: + return Debug.fail(`Unexpected SyntaxKind: ${decl.kind}`); + } + } + + function transformJSDocType(node: Node): Node | undefined { + if (node === undefined) { + return undefined; + } + switch (node.kind) { + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + return createTypeReferenceNode("any", emptyArray); + case SyntaxKind.JSDocOptionalType: + return transformJSDocOptionalType(node as JSDocOptionalType); + case SyntaxKind.JSDocNonNullableType: + return transformJSDocType((node as JSDocNonNullableType).type); + case SyntaxKind.JSDocNullableType: + return transformJSDocNullableType(node as JSDocNullableType); + case SyntaxKind.JSDocVariadicType: + return transformJSDocVariadicType(node as JSDocVariadicType); + case SyntaxKind.JSDocFunctionType: + return transformJSDocFunctionType(node as JSDocFunctionType); + case SyntaxKind.Parameter: + return transformJSDocParameter(node as ParameterDeclaration); + case SyntaxKind.TypeReference: + return transformJSDocTypeReference(node as TypeReferenceNode); + default: + return visitEachChild(node, transformJSDocType, /*context*/ undefined) as TypeNode; + } + } + + function transformJSDocOptionalType(node: JSDocOptionalType) { + return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("undefined", emptyArray)]); + } + + function transformJSDocNullableType(node: JSDocNullableType) { + return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("null", emptyArray)]); + } + + function transformJSDocVariadicType(node: JSDocVariadicType) { + return createArrayTypeNode(visitNode(node.type, transformJSDocType)); + } + + function transformJSDocFunctionType(node: JSDocFunctionType) { + const parameters = node.parameters && node.parameters.map(transformJSDocType); + return createFunctionTypeNode(emptyArray, parameters as ParameterDeclaration[], node.type); + } + + function transformJSDocParameter(node: ParameterDeclaration) { + const index = node.parent.parameters.indexOf(node); + const isRest = node.type.kind === SyntaxKind.JSDocVariadicType && index === node.parent.parameters.length - 1; + const name = node.name || (isRest ? "rest" : "arg" + index); + const dotdotdot = isRest ? createToken(SyntaxKind.DotDotDotToken) : node.dotDotDotToken; + return createParameter(node.decorators, node.modifiers, dotdotdot, name, node.questionToken, visitNode(node.type, transformJSDocType), node.initializer); + } + + function transformJSDocTypeReference(node: TypeReferenceNode) { + let name = node.typeName; + let args = node.typeArguments; + if (isIdentifier(node.typeName)) { + let text = node.typeName.text; + switch (node.typeName.text) { + case "String": + case "Boolean": + case "Object": + case "Number": + text = text.toLowerCase(); + break; + case "array": + case "date": + case "promise": + text = text[0].toUpperCase() + text.slice(1); + break; + } + name = createIdentifier(text); + if ((text === "Array" || text === "Promise") && !node.typeArguments) { + args = createNodeArray([createTypeReferenceNode("any", emptyArray)]); + } + else { + args = visitNodes(node.typeArguments, transformJSDocType); + } + } + return createTypeReferenceNode(name, args); + } +} diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index 1b02b11678bf4..e9f229fa0d78f 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -269,4 +269,4 @@ namespace ts.refactor.convertFunctionToES6Class { return filter(source.modifiers, modifier => modifier.kind === kind); } } -} \ No newline at end of file +} diff --git a/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts index 680b7f8b02fbe..21aeda7ae5278 100644 --- a/src/services/refactors/refactors.ts +++ b/src/services/refactors/refactors.ts @@ -1,2 +1,3 @@ +/// /// /// diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts new file mode 100644 index 0000000000000..99cf5f8db1c15 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts @@ -0,0 +1,10 @@ +/// + +// @Filename: test123.ts +/////** @type {number} */ +////var /*1*/x; + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** @type {number} */ +var x: number;`, 'Annotate with type from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts new file mode 100644 index 0000000000000..0553a411c2e6f --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts @@ -0,0 +1,15 @@ +/// + +/////** +//// * @param {?} x +//// * @returns {number} +//// */ +////var f = /*1*/(/*2*/x) => x + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {?} x + * @returns {number} + */ +var f = (x: any): number => x`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts new file mode 100644 index 0000000000000..aaa7aaefa5b7e --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts @@ -0,0 +1,15 @@ +/// + +/////** +//// * @param {?} x +//// * @returns {number} +//// */ +////var f = /*1*/(/*2*/x) => x + +verify.applicableRefactorAvailableAtMarker('2'); +verify.fileAfterApplyingRefactorAtMarker('2', +`/** + * @param {?} x + * @returns {number} + */ +var f = (x: any): number => x`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts new file mode 100644 index 0000000000000..e541bad4e4f29 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts @@ -0,0 +1,18 @@ +/// + +////class C { +//// /** +//// * @return {...*} +//// */ +//// /*1*/m(x) { +//// } +////} +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`class C { + /** + * @return {...*} + */ + m(x): any[] { + } +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts new file mode 100644 index 0000000000000..a47c090cd17e9 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts @@ -0,0 +1,11 @@ +/// +////class C { +//// /** @return {number} */ +//// get /*1*/c() { return 12 } +////} +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`class C { + /** @return {number} */ + get c(): number { return 12; } +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts new file mode 100644 index 0000000000000..13b4591de4764 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts @@ -0,0 +1,11 @@ +/// +/////** @return {number} */ +////function f() { +//// /*1*/return 12; +////} +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** @return {number} */ +function f(): number { + return 12; +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts new file mode 100644 index 0000000000000..c316430b71803 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts @@ -0,0 +1,30 @@ +/// +// @strict: true +/////** +//// * @param {Boolean} x +//// * @param {String} y +//// * @param {Number} z +//// * @param {Object} alpha +//// * @param {date} beta +//// * @param {promise} gamma +//// * @param {array} delta +//// * @param {Array} epsilon +//// * @param {promise} zeta +//// */ +////function f(/*1*/x, /*2*/y, /*3*/z, /*4*/alpha, /*5*/beta, /*6*/gamma, /*7*/delta, /*8*/epsilon, /*9*/zeta) { +////} +verify.applicableRefactorAvailableAtMarker('9'); +verify.fileAfterApplyingRefactorAtMarker('9', +`/** + * @param {Boolean} x + * @param {String} y + * @param {Number} z + * @param {Object} alpha + * @param {date} beta + * @param {promise} gamma + * @param {array} delta + * @param {Array} epsilon + * @param {promise} zeta + */ +function f(x: boolean, y: string, z: number, alpha: object, beta: Date, gamma: Promise, delta: Array, epsilon: Array, zeta: Promise) { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts new file mode 100644 index 0000000000000..a332a84d9109d --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts @@ -0,0 +1,9 @@ +/// +// @strict: true +/////** @type {function(*, ...number, ...boolean): void} */ +////var /*1*/x = (x, ys, ...zs) => { }; + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** @type {function(*, ...number, ...boolean): void} */ +var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { };`, 'Annotate with type from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts new file mode 100644 index 0000000000000..ed3180d3bd8be --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts @@ -0,0 +1,18 @@ +/// +////class C { +//// /** +//// * @param {number} x - the first parameter +//// */ +//// constructor(/*1*/x) { +//// } +////} +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`class C { + /** + * @param {number} x - the first parameter + */ + constructor(x: number) { + } +}`, 'Annotate with types from JSDoc', 'annotate'); + diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts new file mode 100644 index 0000000000000..9cfcb5c2471f4 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts @@ -0,0 +1,11 @@ +/// +////class C { +//// /** @param {number} value */ +//// set c(/*1*/value) { return 12 } +////} +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`class C { + /** @param {number} value */ + set c(value: number) { return 12; } +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts new file mode 100644 index 0000000000000..9e7ef6d25fd63 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts @@ -0,0 +1,19 @@ +/// +// @strict: true +/////** +//// * @template T +//// * @param {number} a +//// * @param {T} b +//// */ +////function /*1*/f(a, b) { +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @template T + * @param {number} a + * @param {T} b + */ +function f(a: number, b: T) { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts new file mode 100644 index 0000000000000..85f28de4a9973 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts @@ -0,0 +1,6 @@ +/// + +// @Filename: test123.ts +/////** @type {number} */ +////var /*1*/x: string; +verify.not.applicableRefactorAvailableAtMarker('1'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts new file mode 100644 index 0000000000000..a45eb788c738c --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts @@ -0,0 +1,17 @@ +/// +// @strict: true +/////** +//// * @param {number} a +//// * @param {T} b +//// */ +////function /*1*/f(a, b) { +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {number} a + * @param {T} b + */ +function f(a: number, b: T) { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts new file mode 100644 index 0000000000000..ca5dbe312e3a0 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts @@ -0,0 +1,28 @@ +/// +/////** +//// * @param {number} x - the first parameter +//// * @param {{ a: string, b: Date }} y - the most complex parameter +//// * @param z - the best parameter +//// * @param alpha - the other best parameter +//// * @param {*} beta - I have no idea how this got here +//// */ +////function f(/*1*/x, /*2*/y, /*3*/z: string, /*4*/alpha, /*5*/beta) { +////} + +verify.not.applicableRefactorAvailableAtMarker('3'); +verify.not.applicableRefactorAvailableAtMarker('4'); +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {number} x - the first parameter + * @param {{ a: string, b: Date }} y - the most complex parameter + * @param z - the best parameter + * @param alpha - the other best parameter + * @param {*} beta - I have no idea how this got here + */ +function f(x: number, y: { + a: string; + b: Date; +}, z: string, alpha, beta: any) { +}`, 'Annotate with types from JSDoc', 'annotate'); + diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts new file mode 100644 index 0000000000000..2a2d7f943e2e3 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts @@ -0,0 +1,29 @@ +/// +// @strict: true +/////** +//// * @param {*} x +//// * @param {?} y +//// * @param {number=} z +//// * @param {...number} alpha +//// * @param {function(this:{ a: string}, string, number): boolean} beta +//// * @param {number?} gamma +//// * @param {number!} delta +//// */ +////function f(/*1*/x, /*2*/y, /*3*/z, /*4*/alpha, /*5*/beta, /*6*/gamma, /*7*/delta) { +////} + +verify.applicableRefactorAvailableAtMarker('5'); +verify.fileAfterApplyingRefactorAtMarker('5', +`/** + * @param {*} x + * @param {?} y + * @param {number=} z + * @param {...number} alpha + * @param {function(this:{ a: string}, string, number): boolean} beta + * @param {number?} gamma + * @param {number!} delta + */ +function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { + a: string; +}, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts new file mode 100644 index 0000000000000..1ae2949ef3ca4 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts @@ -0,0 +1,13 @@ +/// + +////class C { +//// /** @type {number | null} */ +//// /*1*/p = null +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`class C { + /** @type {number | null} */ + p: number | null = null; +}`, 'Annotate with type from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts new file mode 100644 index 0000000000000..53e8170533df6 --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts @@ -0,0 +1,13 @@ +/// + +////declare class C { +//// /** @type {number | null} */ +//// /*1*/p; +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`declare class C { + /** @type {number | null} */ + p: number | null; +}`, 'Annotate with type from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts new file mode 100644 index 0000000000000..a99ea8233894d --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts @@ -0,0 +1,17 @@ +/// + +/////** +//// * @param {number} x +//// * @returns {number} +//// */ +/////*1*/function f(x) { +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {number} x + * @returns {number} + */ +function f(x: number): number { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts new file mode 100644 index 0000000000000..72ddb36988e0a --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts @@ -0,0 +1,17 @@ +/// + +/////** +//// * @param {number} x +//// * @returns {number} +//// */ +////var f = /*1*/function (x) { +////} + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {number} x + * @returns {number} + */ +var f = function(x: number): number { +}`, 'Annotate with types from JSDoc', 'annotate'); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts new file mode 100644 index 0000000000000..6b967e8e004ac --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts @@ -0,0 +1,15 @@ +/// + +/////** +//// * @param {?} x +//// * @returns {number} +//// */ +////var f = /*1*/x => x + +verify.applicableRefactorAvailableAtMarker('1'); +verify.fileAfterApplyingRefactorAtMarker('1', +`/** + * @param {?} x + * @returns {number} + */ +var f = (x: any): number => x`, 'Annotate with types from JSDoc', 'annotate');