Komplexní průvodce TypeScript Compiler API, pokrývající abstraktní syntaktické stromy (AST), analýzu kódu, transformaci a generování pro mezinárodní vývojáře.
TypeScript Compiler API: Ovládněte manipulaci s AST a transformaci kódu
TypeScript Compiler API poskytuje výkonné rozhraní pro analýzu, manipulaci a generování kódu v jazycích TypeScript a JavaScript. Jeho jádrem je Abstraktní syntaktický strom (AST), což je strukturovaná reprezentace vašeho zdrojového kódu. Porozumění práci s AST otevírá možnosti pro budování pokročilých nástrojů, jako jsou linters, formátovače kódu, statické analyzátory a vlastní generátory kódu.
Co je TypeScript Compiler API?
TypeScript Compiler API je soubor rozhraní a funkcí TypeScript, které zpřístupňují vnitřní fungování kompilátoru TypeScript. Umožňuje vývojářům programově interagovat s procesem kompilace, nad rámec pouhé kompilace kódu. Můžete jej použít k:
- Analýze kódu: Kontrola struktury kódu, identifikace potenciálních problémů a extrakce sémantických informací.
- Transformaci kódu: Úprava existujícího kódu, přidání nových funkcí nebo automatické refaktorování kódu.
- Generování kódu: Vytváření nového kódu od začátku na základě šablon nebo jiných vstupů.
Toto API je nezbytné pro budování sofistikovaných vývojových nástrojů, které zlepšují kvalitu kódu, automatizují opakující se úlohy a zvyšují produktivitu vývojářů.
Porozumění Abstraktnímu syntaktickému stromu (AST)
AST je stromová reprezentace struktury vašeho kódu. Každý uzel ve stromu představuje syntaktickou konstrukci, jako je deklarace proměnné, volání funkce nebo příkaz řízení toku. TypeScript Compiler API poskytuje nástroje pro procházení AST, inspekci jeho uzlů a jejich úpravu.
Zvažte tento jednoduchý kód v TypeScriptu:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
AST pro tento kód by reprezentovalo deklaraci funkce, návratový příkaz, šablonový řetězec, volání console.log a další prvky kódu. Vizualizace AST může být náročná, ale nástroje jako AST explorer (astexplorer.net) mohou pomoci. Tyto nástroje vám umožní zadat kód a zobrazit jeho odpovídající AST v uživatelsky přívětivém formátu. Používání AST Explorer vám pomůže pochopit strukturu kódu, se kterou budete manipulovat.
Klíčové typy uzlů AST
TypeScript Compiler API definuje různé typy uzlů AST, každý reprezentující jinou syntaktickou konstrukci. Zde jsou některé běžné typy uzlů:
- SourceFile: Reprezentuje celý soubor TypeScript.
- FunctionDeclaration: Reprezentuje definici funkce.
- VariableDeclaration: Reprezentuje deklaraci proměnné.
- Identifier: Reprezentuje identifikátor (např. název proměnné, název funkce).
- StringLiteral: Reprezentuje literál řetězce.
- CallExpression: Reprezentuje volání funkce.
- ReturnStatement: Reprezentuje návratový příkaz.
Každý typ uzlu má vlastnosti, které poskytují informace o odpovídajícím prvku kódu. Například uzel `FunctionDeclaration` může mít vlastnosti pro jeho název, parametry, návratový typ a tělo.
Začínáme s Compiler API
Abyste mohli začít používat Compiler API, budete si muset nainstalovat TypeScript a mít základní porozumění syntaxi TypeScriptu. Zde je jednoduchý příklad, který ukazuje, jak číst soubor TypeScript a tisknout jeho AST:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015, // Cílová verze ECMAScriptu
true // SetParentNodes: true pro zachování referencí na rodiče v AST
);
function printAST(node: ts.Node, indent = 0) {
const indentStr = " ".repeat(indent);
console.log(`${indentStr}${ts.SyntaxKind[node.kind]}`);
node.forEachChild(child => printAST(child, indent + 1));
}
printAST(sourceFile);
Vysvětlení:
- Import modulů: Importuje modul `typescript` a modul `fs` pro operace se souborovým systémem.
- Čtení zdrojového souboru: Přečte obsah souboru TypeScript s názvem `example.ts`. Abyste to mohli spustit, budete muset vytvořit soubor `example.ts`.
- Vytvoření SourceFile: Vytvoří objekt `SourceFile`, který reprezentuje kořen AST. Funkce `ts.createSourceFile` analyzuje zdrojový kód a generuje AST.
- Tisk AST: Definuje rekurzivní funkci `printAST`, která prochází AST a tiskne typ každého uzlu.
- Volání printAST: Volá `printAST` pro zahájení tisku AST z kořenového uzlu `SourceFile`.
Chcete-li tento kód spustit, uložte jej jako soubor `.ts` (např. `ast-example.ts`), vytvořte soubor `example.ts` s nějakým kódem v jazyce TypeScript a poté kód zkompilujte a spusťte:
tsc ast-example.ts
node ast-example.js
Tím se na konzoli vypíše AST vašeho souboru `example.ts`. Výstup zobrazí hierarchii uzlů a jejich typy. Například může zobrazit typy uzlů jako `FunctionDeclaration`, `Identifier`, `Block` a další.
Procházení AST
Compiler API poskytuje několik způsobů, jak procházet AST. Nejjednodušší je použít metodu `forEachChild`, jak je ukázáno v předchozím příkladu. Tato metoda navštíví každý podřízený uzel daného uzlu.
Pro složitější scénáře procházení můžete použít vzor `Visitor` (návštěvník). Návštěvník je objekt, který definuje metody, které se mají volat pro konkrétní typy uzlů. To vám umožní přizpůsobit proces procházení a provádět akce na základě typu uzlu.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
class IdentifierVisitor {
visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
console.log(`Found identifier: ${node.text}`);
}
ts.forEachChild(node, n => this.visit(n));
}
}
const visitor = new IdentifierVisitor();
visitor.visit(sourceFile);
Vysvětlení:
- Třída IdentifierVisitor: Definuje třídu `IdentifierVisitor` s metodou `visit`.
- Metoda Visit: Metoda `visit` kontroluje, zda je aktuální uzel `Identifier`. Pokud ano, vypíše text identifikátoru. Poté rekurzivně volá `ts.forEachChild` pro návštěvu podřízených uzlů.
- Vytvoření návštěvníka: Vytvoří instanci `IdentifierVisitor`.
- Zahájení procházení: Volá metodu `visit` na `SourceFile` pro zahájení procházení.
Tento příklad ukazuje, jak najít všechny identifikátory v AST. Tento vzor můžete přizpůsobit pro nalezení jiných typů uzlů a provedení jiných akcí.
Transformace AST
Skutečná síla Compiler API spočívá v jeho schopnosti transformovat AST. AST můžete upravovat pro změnu struktury a chování vašeho kódu. To je základ pro nástroje pro refaktorování kódu, generátory kódu a další pokročilé nástroje.
K transformaci AST budete muset použít funkci `ts.transform`. Tato funkce přijímá `SourceFile` a seznam funkcí `TransformerFactory`. `TransformerFactory` je funkce, která přijímá `TransformationContext` a vrací funkci `Transformer`. Funkce `Transformer` je zodpovědná za návštěvu a transformaci uzlů v AST.
Zde je jednoduchý příklad, který ukazuje, jak přidat komentář na začátek souboru TypeScript:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
const transformerFactory: ts.TransformerFactory = context => {
return transformer => {
return node => {
if (ts.isSourceFile(node)) {
// Vytvoření komentáře na začátku
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" This file was automatically transformed ",
true // má koncové nové řádky
);
return node;
}
return node;
};
};
};
const { transformed } = ts.transform(sourceFile, [transformerFactory]);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed
});
const result = printer.printFile(transformed[0]);
fs.writeFileSync("example.transformed.ts", result);
Vysvětlení:
- TransformerFactory: Definuje funkci `TransformerFactory`, která vrací funkci `Transformer`.
- Transformer: Funkce `Transformer` kontroluje, zda je aktuální uzel `SourceFile`. Pokud ano, přidá k uzlu komentář na začátek pomocí `ts.addSyntheticLeadingComment`.
- ts.transform: Volá `ts.transform` pro aplikaci transformace na `SourceFile`.
- Printer: Vytvoří objekt `Printer` pro generování kódu z transformovaného AST.
- Tisk a zápis: Vytiskne transformovaný kód a zapíše jej do nového souboru s názvem `example.transformed.ts`.
Tento příklad ukazuje jednoduchou transformaci, ale můžete použít stejný vzor pro provádění složitějších transformací, jako je refaktorování kódu, přidávání logovacích příkazů nebo generování dokumentace.
Pokročilé techniky transformace
Zde jsou některé pokročilé techniky transformace, které můžete použít s Compiler API:
- Vytváření nových uzlů: Použijte funkce `ts.createXXX` pro vytváření nových uzlů AST. Například `ts.createVariableDeclaration` vytvoří nový uzel deklarace proměnné.
- Nahrazování uzlů: Nahraďte existující uzly novými uzly pomocí funkce `ts.visitEachChild`.
- Přidávání uzlů: Přidejte nové uzly do AST pomocí funkcí `ts.updateXXX`. Například `ts.updateBlock` aktualizuje příkaz bloku novými příkazy.
- Odstraňování uzlů: Odstraňte uzly z AST vrácením `undefined` z transformační funkce.
Generování kódu
Po transformaci AST budete muset z něj generovat kód. Compiler API pro tento účel poskytuje objekt `Printer`. `Printer` přijímá AST a generuje řetězcovou reprezentaci kódu.
Funkce `ts.createPrinter` vytváří objekt `Printer`. Tiskárnu můžete nakonfigurovat pomocí různých možností, jako je znak nového řádku, který se má použít, a zda se mají emitovat komentáře.
Metoda `printer.printFile` přijímá `SourceFile` a vrací řetězcovou reprezentaci kódu. Tento řetězec pak můžete zapsat do souboru.
Praktické aplikace Compiler API
TypeScript Compiler API má mnoho praktických aplikací v softwarovém vývoji. Zde je několik příkladů:
- Linters: Budujte vlastní linters pro vynucování kódovacích standardů a identifikaci potenciálních problémů ve vašem kódu.
- Formátovače kódu: Vytvářejte formátovače kódu pro automatické formátování vašeho kódu podle specifického stylu.
- Statické analyzátory: Vyvíjejte statické analyzátory pro detekci chyb, bezpečnostních zranitelností a výkonnostních úzkých míst ve vašem kódu.
- Generátory kódu: Generujte kód ze šablon nebo jiných vstupů, automatizujte opakující se úlohy a snižujte množství opakovaného kódu. Například nástroj může generovat klienty API nebo schémata databáze z popisného souboru.
- Nástroje pro refaktorování: Budujte nástroje pro refaktorování pro automatické přejmenování proměnných, extrakci funkcí nebo přesun kódu mezi soubory.
- Automatizace internacionalizace (i18n): Automaticky extrahujte přeložitelné řetězce z vašeho kódu v jazyce TypeScript a generujte lokalizační soubory pro různé jazyky. Například nástroj by mohl skenovat kód pro řetězce předané funkci `translate()` a automaticky je přidat do souboru s lokalizačními zdroji.
Příklad: Vytvoření jednoduchého Linteru
Vytvoříme jednoduchý linter, který kontroluje nepoužívané proměnné v kódu TypeScript. Tento linter identifikuje proměnné, které jsou deklarovány, ale nikdy použity.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
function findUnusedVariables(sourceFile: ts.SourceFile) {
const usedVariables = new Set();
function visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
usedVariables.add(node.text);
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
const unusedVariables: string[] = [];
function checkVariableDeclaration(node: ts.Node) {
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
const variableName = node.name.text;
if (!usedVariables.has(variableName)) {
unusedVariables.push(variableName);
}
}
ts.forEachChild(node, checkVariableDeclaration);
}
checkVariableDeclaration(sourceFile);
return unusedVariables;
}
const unusedVariables = findUnusedVariables(sourceFile);
if (unusedVariables.length > 0) {
console.log("Unused variables:");
unusedVariables.forEach(variable => console.log(`- ${variable}`));
} else {
console.log("No unused variables found.");
}
Vysvětlení:
- Funkce findUnusedVariables: Definuje funkci `findUnusedVariables`, která přijímá `SourceFile` jako vstup.
- Množina usedVariables: Vytvoří `Set` pro ukládání názvů použitých proměnných.
- Funkce visit: Definuje rekurzivní funkci `visit`, která prochází AST a přidává názvy všech identifikátorů do množiny `usedVariables`.
- Funkce checkVariableDeclaration: Definuje rekurzivní funkci `checkVariableDeclaration`, která kontroluje, zda je deklarace proměnné nepoužitá. Pokud ano, přidá název proměnné do pole `unusedVariables`.
- Vrácení unusedVariables: Vrací pole obsahující názvy všech nepoužitých proměnných.
- Výstup: Vypíše nepoužité proměnné na konzoli.
Tento příklad ukazuje jednoduchý linter. Můžete jej rozšířit pro kontrolu dalších kódovacích standardů a identifikaci dalších potenciálních problémů ve vašem kódu. Například byste mohli kontrolovat nepoužité importy, příliš složité funkce nebo potenciální bezpečnostní zranitelnosti. Klíčem je pochopit, jak procházet AST a identifikovat konkrétní typy uzlů, o které máte zájem.
Osvědčené postupy a úvahy
- Porozumějte AST: Věnujte čas pochopení struktury AST. Použijte nástroje jako AST explorer k vizualizaci AST vašeho kódu.
- Používejte typové strážce (Type Guards): Používejte typové strážce (`ts.isXXX`), abyste zajistili, že pracujete se správnými typy uzlů.
- Zvažte výkon: Transformace AST mohou být výpočetně náročné. Optimalizujte svůj kód, abyste minimalizovali počet uzlů, které navštěvujete a transformujete.
- Zpracovávejte chyby: Zpracovávejte chyby plynule. Compiler API může vyhazovat výjimky, pokud se pokusíte provést neplatné operace na AST.
- Důkladně testujte: Důkladně otestujte své transformace, abyste zajistili, že produkují požadované výsledky a nezavádějí nové chyby.
- Používejte existující knihovny: Zvažte použití existujících knihoven, které poskytují abstrakce vyšší úrovně nad Compiler API. Tyto knihovny mohou zjednodušit běžné úlohy a snížit množství kódu, který musíte napsat. Příklady zahrnují `ts-morph` a `typescript-eslint`.
Závěr
TypeScript Compiler API je výkonný nástroj pro budování pokročilých vývojových nástrojů. Pochopením práce s AST můžete vytvářet linters, formátovače kódu, statické analyzátory a další nástroje, které zlepšují kvalitu kódu, automatizují opakující se úlohy a zvyšují produktivitu vývojářů. Ačkoli API může být složité, výhody jeho zvládnutí jsou významné. Tento komplexní průvodce poskytuje základ pro efektivní průzkum a využití Compiler API ve vašich projektech. Nezapomeňte využívat nástroje jako AST Explorer, pečlivě zpracovávat typy uzlů a důkladně testovat své transformace. S praxí a odhodláním můžete odemknout plný potenciál TypeScript Compiler API a budovat inovativní řešení pro krajinu softwarového vývoje.
Další průzkum:
- Dokumentace TypeScript Compiler API: [https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API)
- AST Explorer: [https://astexplorer.net/](https://astexplorer.net/)
- Knihovna ts-morph: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)