diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a74d39fd07d3d..d8545e015290d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3830,6 +3830,10 @@ "category": "Suggestion", "code": 80003 }, + "JSDoc types may be moved to TypeScript types.": { + "category": "Suggestion", + "code": 80004 + }, "Add missing 'super()' call": { "category": "Message", diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/codefixes/annotateWithTypeFromJSDoc.ts similarity index 64% rename from src/services/refactors/annotateWithTypeFromJSDoc.ts rename to src/services/codefixes/annotateWithTypeFromJSDoc.ts index 3258da4636a51..c92fabeab96f7 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/codefixes/annotateWithTypeFromJSDoc.ts @@ -1,108 +1,66 @@ /* @internal */ -namespace ts.refactor.annotateWithTypeFromJSDoc { - const refactorName = "Annotate with type from JSDoc"; - const actionName = "annotate"; - const description = Diagnostics.Annotate_with_type_from_JSDoc.message; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); +namespace ts.codefix { + const fixId = "annotateWithTypeFromJSDoc"; + const errorCodes = [Diagnostics.JSDoc_types_may_be_moved_to_TypeScript_types.code]; + registerCodeFix({ + errorCodes, + getCodeActions(context) { + const decl = getDeclaration(context.sourceFile, context.span.start); + if (!decl) return; + const description = getLocaleSpecificMessage(Diagnostics.Annotate_with_type_from_JSDoc); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, decl)); + return [{ description, changes, fixId }]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { + const decl = getDeclaration(diag.file!, diag.start!); + if (decl) doChange(changes, diag.file!, decl); + }), + }); + + function getDeclaration(file: SourceFile, pos: number): DeclarationWithType | undefined { + const name = getTokenAtPosition(file, pos, /*includeJsDocComment*/ false); + // For an arrow function with no name, 'name' lands on the first parameter. + return tryCast(isParameter(name.parent) ? name.parent.parent : name.parent, parameterShouldGetTypeFromJSDoc); + } type DeclarationWithType = | FunctionLikeDeclaration | VariableDeclaration - | ParameterDeclaration | PropertySignature | PropertyDeclaration; - function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { - if (isInJavaScriptFile(context.file)) { - return undefined; - } - - const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false); - if (hasUsableJSDoc(findAncestor(node, isDeclarationWithType))) { - return [{ - name: refactorName, - description, - actions: [ - { - description, - name: actionName - } - ] - }]; - } + export function parameterShouldGetTypeFromJSDoc(node: Node): node is DeclarationWithType { + return isDeclarationWithType(node) && hasUsableJSDoc(node); } - 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 hasUsableJSDoc(decl: DeclarationWithType | ParameterDeclaration): boolean { + return isFunctionLikeDeclaration(decl) + ? decl.parameters.some(hasUsableJSDoc) || (!decl.type && !!getJSDocReturnType(decl)) + : !decl.type && !!getJSDocType(decl); } - function getEditsForAction(context: RefactorContext, action: string): RefactorEditInfo | undefined { - if (actionName !== action) { - return Debug.fail(`actionName !== action: ${actionName} !== ${action}`); - } - 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 && decl.kind === SyntaxKind.Parameter) { - return getEditsForFunctionAnnotation(context); - } - else if (jsdocType) { - return getEditsForAnnotation(context); + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, decl: DeclarationWithType): void { + if (isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p)))) { + findAncestor(decl, isFunctionLike); + const fn = findAncestor(decl, isFunctionLikeDeclaration); + const functionWithType = addTypesToFunctionLike(fn); + suppressLeadingAndTrailingTrivia(functionWithType); + changes.replaceNode(sourceFile, fn, functionWithType, textChanges.useNonAdjustedPositions); + return; } else { - Debug.assert(!!refactor, "No applicable refactor found."); - } - } - - function getEditsForAnnotation(context: RefactorContext): RefactorEditInfo | undefined { - 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 jsdocType = Debug.assertDefined(getJSDocType(decl)); // If not defined, shouldn't have been an error to fix + Debug.assert(!decl.type); // If defined, shouldn't have been an error to fix. + const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); + suppressLeadingAndTrailingTrivia(declarationWithType); + changes.replaceNode(sourceFile, decl, declarationWithType, textChanges.useNonAdjustedPositions); } - - const changeTracker = textChanges.ChangeTracker.fromContext(context); - const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); - suppressLeadingAndTrailingTrivia(declarationWithType); - changeTracker.replaceNode(sourceFile, decl, declarationWithType, textChanges.useNonAdjustedPositions); - return { - edits: changeTracker.getChanges(), - renameFilename: undefined, - renameLocation: undefined - }; - } - - function getEditsForFunctionAnnotation(context: RefactorContext): RefactorEditInfo | undefined { - 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.replaceNode(sourceFile, decl, functionWithType, textChanges.useNonAdjustedPositions); - 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; } diff --git a/src/services/codefixes/fixes.ts b/src/services/codefixes/fixes.ts index 43313011bd22c..50a0afd392500 100644 --- a/src/services/codefixes/fixes.ts +++ b/src/services/codefixes/fixes.ts @@ -1,4 +1,5 @@ /// +/// /// /// /// diff --git a/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts index 0e5158563c83a..da2f08a8e9fcf 100644 --- a/src/services/refactors/refactors.ts +++ b/src/services/refactors/refactors.ts @@ -1,2 +1 @@ -/// /// diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 602b7e2529783..3de43d7d40f92 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -9,21 +9,28 @@ namespace ts { diags.push(createDiagnosticForNode(sourceFile.commonJsModuleIndicator, Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module)); } + const isJsFile = isSourceFileJavaScript(sourceFile); + function check(node: Node) { switch (node.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: - const symbol = node.symbol; - if (symbol.members && (symbol.members.size > 0)) { - diags.push(createDiagnosticForNode(isVariableDeclaration(node.parent) ? node.parent.name : node, Diagnostics.This_constructor_function_may_be_converted_to_a_class_declaration)); + if (isJsFile) { + const symbol = node.symbol; + if (symbol.members && (symbol.members.size > 0)) { + diags.push(createDiagnosticForNode(isVariableDeclaration(node.parent) ? node.parent.name : node, Diagnostics.This_constructor_function_may_be_converted_to_a_class_declaration)); + } } break; } + + if (!isJsFile && codefix.parameterShouldGetTypeFromJSDoc(node)) { + diags.push(createDiagnosticForNode(node.name || node, Diagnostics.JSDoc_types_may_be_moved_to_TypeScript_types)); + } + node.forEachChild(check); } - if (isInJavaScriptFile(sourceFile)) { - check(sourceFile); - } + check(sourceFile); if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) { for (const importNode of sourceFile.imports) { diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts index 99cf5f8db1c15..ef626fd358d52 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts @@ -2,9 +2,16 @@ // @Filename: test123.ts /////** @type {number} */ -////var /*1*/x; +////var [|x|]; -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.getSuggestionDiagnostics([{ + message: "JSDoc types may be moved to TypeScript types.", + code: 80004, +}]); + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** @type {number} */ -var x: number;`, 'Annotate with type from JSDoc', 'annotate'); +var x: number;`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts index f90c61c33d94d..2bd1df5c6e6b3 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts @@ -4,12 +4,14 @@ //// * @param {?} x //// * @returns {number} //// */ -////var f = /*1*/(/*2*/x) => x +////var f = (x) => x -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {?} x * @returns {number} */ -var f = (x: any): number => x`, 'Annotate with type from JSDoc', 'annotate'); +var f = (x: any): number => x`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts index f0ab5f3013e5b..2bd1df5c6e6b3 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts @@ -4,12 +4,14 @@ //// * @param {?} x //// * @returns {number} //// */ -////var f = /*1*/(/*2*/x) => x +////var f = (x) => x -verify.applicableRefactorAvailableAtMarker('2'); -verify.fileAfterApplyingRefactorAtMarker('2', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {?} x * @returns {number} */ -var f = (x: any): number => x`, 'Annotate with type from JSDoc', 'annotate'); +var f = (x: any): number => x`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts index fc9b7c2a17eae..e7893fd99046a 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts @@ -4,15 +4,18 @@ //// /** //// * @return {...*} //// */ -//// /*1*/m(x) { +//// m(x) { //// } ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `class C { /** * @return {...*} */ m(x): any[] { } -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts index da147548b2775..0f4b8596d7e26 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc13.ts @@ -1,11 +1,14 @@ /// ////class C { //// /** @return {number} */ -//// get /*1*/c() { return 12 } +//// get c() { return 12 } ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `class C { /** @return {number} */ get c(): number { return 12; } -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts index 5020d0f11cf1b..a8a73a9a620f5 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc14.ts @@ -1,11 +1,14 @@ /// /////** @return {number} */ ////function f() { -//// /*1*/return 12; +//// return 12; ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** @return {number} */ function f(): number { return 12; -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts index 1389e800f5a71..8072f0547de67 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts @@ -11,10 +11,12 @@ //// * @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) { +////function f(x, y, z, alpha, beta, gamma, delta, epsilon, zeta) { ////} -verify.applicableRefactorAvailableAtMarker('9'); -verify.fileAfterApplyingRefactorAtMarker('9', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {Boolean} x * @param {String} y @@ -27,4 +29,5 @@ verify.fileAfterApplyingRefactorAtMarker('9', * @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 type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts index a332a84d9109d..ab57b32aaacc2 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts @@ -1,9 +1,11 @@ /// // @strict: true /////** @type {function(*, ...number, ...boolean): void} */ -////var /*1*/x = (x, ys, ...zs) => { }; +////var x = (x, ys, ...zs) => { }; -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** @type {function(*, ...number, ...boolean): void} */ -var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { };`, 'Annotate with type from JSDoc', 'annotate'); +var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { };`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts index ec26a9d1de7bb..2382cc18a272f 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts @@ -3,16 +3,18 @@ //// /** //// * @param {number} x - the first parameter //// */ -//// constructor(/*1*/x) { +//// constructor(x) { //// } ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `class C { /** * @param {number} x - the first parameter */ constructor(x: number) { } -}`, 'Annotate with type from JSDoc', 'annotate'); - +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts index 10d3192583dac..844015d456e3a 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts @@ -1,11 +1,14 @@ /// ////class C { //// /** @param {number} value */ -//// set c(/*1*/value) { return 12 } +//// set c(value) { return 12 } ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `class C { /** @param {number} value */ set c(value: number) { return 12; } -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts index a47522fe03ca1..96e351ed2baa6 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts @@ -5,15 +5,17 @@ //// * @param {number} a //// * @param {T} b //// */ -////function /*1*/f(a, b) { +////function f(a, b) { ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @template T * @param {number} a * @param {T} b */ function f(a: number, b: T) { -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts index 85f28de4a9973..8c5fe0b459547 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc2.ts @@ -2,5 +2,6 @@ // @Filename: test123.ts /////** @type {number} */ -////var /*1*/x: string; -verify.not.applicableRefactorAvailableAtMarker('1'); +////var [|x|]: string; + +verify.getSuggestionDiagnostics([]); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts index 093966231fdd2..a54936b1ea610 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc20.ts @@ -7,11 +7,13 @@ ////function /*1*/f(a, b) { ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {number} a * @param {T} b */ function f(a: number, b: T) { -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc21.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc21.ts index b54f83070c4ab..67d7e09994994 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc21.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc21.ts @@ -3,19 +3,19 @@ /////** //// * @return {number} //// */ -////function /*1*/f(x, y) { +////function [|f|](x, y) { ////} //// /////** //// * @return {number} //// */ -////function /*2*/g(x, y): number { +////function g(x, y): number { //// return 0; ////} /////** //// * @param {number} x //// */ -////function /*3*/h(x: number, y): number { +////function h(x: number, y): number { //// return 0; ////} //// @@ -23,22 +23,25 @@ //// * @param {number} x //// * @param {string} y //// */ -////function /*4*/i(x: number, y: string) { +////function i(x: number, y: string) { ////} /////** //// * @param {number} x //// * @return {boolean} //// */ -////function /*5*/j(x: number, y): boolean { +////function j(x: number, y): boolean { //// return true; ////} -verify.not.applicableRefactorAvailableAtMarker('2'); -verify.not.applicableRefactorAvailableAtMarker('3'); -verify.not.applicableRefactorAvailableAtMarker('4'); -verify.not.applicableRefactorAvailableAtMarker('5'); -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +// Only first location triggers a suggestion +verify.getSuggestionDiagnostics([{ + message: "JSDoc types may be moved to TypeScript types.", + code: 80004, +}]); + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @return {number} */ @@ -70,4 +73,5 @@ function i(x: number, y: string) { */ function j(x: number, y): boolean { return true; -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts index d00705848c642..9da43e5099a6f 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts @@ -3,12 +3,15 @@ //// /////** @param {Object} sb //// * @param {Object} ns */ -////function /*1*/f(sb, ns) { +////function f(sb, ns) { ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: ` /** @param {Object} sb * @param {Object} ns */ function f(sb: { [s: string]: boolean; }, ns: { [n: number]: string; }) { -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts index 9413e6046c9e4..0eedc937198b4 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts @@ -6,13 +6,17 @@ //// * @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) { +////function [|f|](x, y, z: string, alpha, beta) { ////} -verify.not.applicableRefactorAvailableAtMarker('3'); -verify.not.applicableRefactorAvailableAtMarker('4'); -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.getSuggestionDiagnostics([{ + message: "JSDoc types may be moved to TypeScript types.", + code: 80004, +}]); + +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {number} x - the first parameter * @param {{ a: string, b: Date }} y - the most complex parameter @@ -21,5 +25,5 @@ verify.fileAfterApplyingRefactorAtMarker('1', * @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 type from JSDoc', 'annotate'); - +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts index ad06bbbc699f2..31825eea3f97f 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts @@ -9,11 +9,12 @@ //// * @param {number?} gamma //// * @param {number!} delta //// */ -////function f(/*1*/x, /*2*/y, /*3*/z, /*4*/alpha, /*5*/beta, /*6*/gamma, /*7*/delta) { +////function [|f|](x, y, z, alpha, beta, gamma, delta) { ////} -verify.applicableRefactorAvailableAtMarker('5'); -verify.fileAfterApplyingRefactorAtMarker('5', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {*} x * @param {?} y @@ -24,4 +25,5 @@ verify.fileAfterApplyingRefactorAtMarker('5', * @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 type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts index 1ae2949ef3ca4..3a072fabed31c 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc5.ts @@ -2,12 +2,14 @@ ////class C { //// /** @type {number | null} */ -//// /*1*/p = null +//// p = null ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `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 index 53e8170533df6..9d11eb8d4457a 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts @@ -2,12 +2,14 @@ ////declare class C { //// /** @type {number | null} */ -//// /*1*/p; +//// p; ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `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 index 68df14c2b52a8..e242b53228d4b 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts @@ -4,14 +4,16 @@ //// * @param {number} x //// * @returns {number} //// */ -/////*1*/function f(x) { +////function f(x) { ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {number} x * @returns {number} */ function f(x: number): number { -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts index 7381b2f288073..1516817b92c16 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts @@ -4,14 +4,16 @@ //// * @param {number} x //// * @returns {number} //// */ -////var f = /*1*/function (x) { +////var f = function (x) { ////} -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {number} x * @returns {number} */ var f = function(x: number): number { -}`, 'Annotate with type from JSDoc', 'annotate'); +}`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts index 704d3681b3d3b..d1517a2c00941 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts @@ -4,12 +4,14 @@ //// * @param {?} x //// * @returns {number} //// */ -////var f = /*1*/x => x +////var f = x => x -verify.applicableRefactorAvailableAtMarker('1'); -verify.fileAfterApplyingRefactorAtMarker('1', +verify.codeFix({ + description: "Annotate with type from JSDoc", + newFileContent: `/** * @param {?} x * @returns {number} */ -var f = (x: any): number => x`, 'Annotate with type from JSDoc', 'annotate'); +var f = (x: any): number => x`, +}); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc_all.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc_all.ts new file mode 100644 index 0000000000000..60e10980bb97b --- /dev/null +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc_all.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: test123.ts +/////** @type {number} */ +////var [|x|]; +/////** @type {string} */ +////var [|y|]; + +verify.codeFixAll({ + fixId: "annotateWithTypeFromJSDoc", + newFileContent: +`/** @type {number} */ +var x: number; +/** @type {string} */ +var y: string;`, +});