1/* @internal */ 2namespace ts.codefix { 3 const fixId = "fixImplicitThis"; 4 const errorCodes = [Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code]; 5 registerCodeFix({ 6 errorCodes, 7 getCodeActions: function getCodeActionsToFixImplicitThis(context) { 8 const { sourceFile, program, span } = context; 9 let diagnostic: DiagnosticAndArguments | undefined; 10 const changes = textChanges.ChangeTracker.with(context, t => { 11 diagnostic = doChange(t, sourceFile, span.start, program.getTypeChecker()); 12 }); 13 return diagnostic ? [createCodeFixAction(fixId, changes, diagnostic, fixId, Diagnostics.Fix_all_implicit_this_errors)] : emptyArray; 14 }, 15 fixIds: [fixId], 16 getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { 17 doChange(changes, diag.file, diag.start, context.program.getTypeChecker()); 18 }), 19 }); 20 21 function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, checker: TypeChecker): DiagnosticAndArguments | undefined { 22 const token = getTokenAtPosition(sourceFile, pos); 23 if (!isThis(token)) return undefined; 24 25 const fn = getThisContainer(token, /*includeArrowFunctions*/ false); 26 if (!isFunctionDeclaration(fn) && !isFunctionExpression(fn)) return undefined; 27 28 if (!isSourceFile(getThisContainer(fn, /*includeArrowFunctions*/ false))) { // 'this' is defined outside, convert to arrow function 29 const fnKeyword = Debug.checkDefined(findChildOfKind(fn, SyntaxKind.FunctionKeyword, sourceFile)); 30 const { name } = fn; 31 const body = Debug.checkDefined(fn.body); // Should be defined because the function contained a 'this' expression 32 if (isFunctionExpression(fn)) { 33 if (name && FindAllReferences.Core.isSymbolReferencedInFile(name, checker, sourceFile, body)) { 34 // Function expression references itself. To fix we would have to extract it to a const. 35 return undefined; 36 } 37 38 // `function() {}` --> `() => {}` 39 changes.delete(sourceFile, fnKeyword); 40 if (name) { 41 changes.delete(sourceFile, name); 42 } 43 changes.insertText(sourceFile, body.pos, " =>"); 44 return [Diagnostics.Convert_function_expression_0_to_arrow_function, name ? name.text : ANONYMOUS]; 45 } 46 else { 47 // `function f() {}` => `const f = () => {}` 48 // `name` should be defined because we only do this in inner contexts, and name is only undefined for `export default function() {}`. 49 changes.replaceNode(sourceFile, fnKeyword, factory.createToken(SyntaxKind.ConstKeyword)); 50 changes.insertText(sourceFile, name!.end, " = "); 51 changes.insertText(sourceFile, body.pos, " =>"); 52 return [Diagnostics.Convert_function_declaration_0_to_arrow_function, name!.text]; 53 } 54 } 55 } 56} 57