Code fix
- TypeScript course
- Expert
- Lesson 29/35
- 5 min
Write a code fix provider.
CodeFixAction structure
A ts.CodeFixAction object carries fixName (the fix identifier), description (the user-facing description) and changes (file changes). changes is a FileTextChanges[]; each entry holds a textChanges array for one file.
const fix: ts.CodeFixAction = {
fixName: "add-any",
description: "Add type annotation",
changes: [{
fileName,
textChanges: [
{ span: { start, length: 0 }, newText: ": any" }
],
}],
};getCodeFixesAtPosition
The language service method that provides code fixes is getCodeFixesAtPosition. It takes a file name, a position range and error codes, and returns a CodeFixAction[]. Inside the plugin you wrap this method and produce extra fixes.
getCodeFixesAtPosition: function(fileName, start, end) {
const prior = ls.getCodeFixesAtPosition(fileName, start, end);
// produce extra fixes
return [...prior, ...fixes];
};Practice Provide a code fix
Write a plugin that wraps getCodeFixesAtPosition. First take ls's fixes (prior). Get the SourceFile. Walk the AST: for each VariableDeclaration without a type annotation, produce a ts.CodeFixAction (fixName: "add-any"). Use textChanges to append : any after the variable name. Merge prior and fixes. Test: source let x = 1;; print the first fix's fixName.
Starter code
import * as ts from "typescript";
// Bir plugin yaz: Code fix metodunu sar
// - Önce ls'nin fixes'ini al (prior)
// - SourceFile'ı al
// - AST'yı gez: tür eklemesi olmayan VariableDeclaration bul
// - Her biri için bir CodeFixAction üret (fixName: "add-any")
// - Metin değişikliği ile ": any" ekleme tanımla
// - prior ve fixes'i birleştir
// Test yaz ve ilk fix'in adını yazdır.
// kodunu buraya yazSample solution
import ts from "typescript";
function createPlugin(info: ts.server.PluginCreateInfo): ts.LanguageService {
const ls = info.languageService;
return {
...ls,
getCodeFixesAtPosition: function(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: ts.FormatCodeSettings, preferences: ts.UserPreferences) {
const prior = ls.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
const sf = ls.getProgram().getSourceFile(fileName);
if (!sf) return prior;
const fixes: ts.CodeFixAction[] = [];
function visit(node: ts.Node) {
if (ts.isVariableDeclaration(node) && !node.type) {
fixes.push({
fixName: "add-any",
description: "Add type annotation",
changes: [{
fileName,
textChanges: [{ span: { start: node.name.getEnd(), length: 0 }, newText: ": any" }],
}],
});
}
ts.forEachChild(node, visit);
}
visit(sf);
return [...prior, ...fixes];
},
};
}
const sf = ts.createSourceFile("app.ts", "let x = 1;", ts.ScriptTarget.Latest, true);
const fakeLs = {
getCodeFixesAtPosition: function() { return [] as ts.CodeFixAction[]; },
getProgram: function() { return { getSourceFile: function(n: string) { return n === "app.ts" ? sf : undefined; } } as unknown as ts.Program; },
} as unknown as ts.LanguageService;
const info = { languageService: fakeLs, config: {} } as unknown as ts.server.PluginCreateInfo;
const plugin = createPlugin(info);
const fixes = plugin.getCodeFixesAtPosition("app.ts", 0, 1, [], {} as ts.FormatCodeSettings, {} as ts.UserPreferences);
console.log(fixes[0].fixName);Expected output
add-any
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What does the changes field of a CodeFixAction contain?
- A single text change
- File changes (FileTextChanges[]) Correct answer
- A diagnostic list
- Source file text
Explanation: The changes field carries a FileTextChanges[]; each entry holds a textChanges array for one file. It is not a single text change, a diagnostic list, or source file text.
2 Fix name
fixes.push({
____: "add-any",
description: "Add type annotation",
changes: [{ fileName, textChanges: [{ span: { start: node.name.getEnd(), length: 0 }, newText: ": any" }] }],
});
fixes.push({
fixName: "add-any",
description: "Add type annotation",
changes: [{ fileName, textChanges: [{ span: { start: node.name.getEnd(), length: 0 }, newText: ": any" }] }],
});
Explanation: CodeFixAction.fixName carries the fix identifier; name, id and fixId do not exist on CodeFixAction.
3 Code fix parts
| Term | Meaning |
|---|---|
fixName |
Fix identifier |
description |
User description |
changes |
File changes |
textChanges |
Text changes |