TypeScript itself provides Compiler apis that can generate operations such as AST corresponding to TypeScript code. As an official compilation tool, it can also be said that it is currently the only available tool. There is an official TypeScript Wiki that describes the Compiler API, but the documentation is light and obscure, with some basic concepts and usage examples added here.
The basic concept
I wrote a previous article about the TypeScript compilation process and some of the internal classes, including some of the classes involved in TypeScript. Here is a brief explanation:
ts.SourceFile
SourceFile represents a SourceFile, which can be understood as the root node of the AST. AST nodes have a base class, ts.node, from which SourceFile inherits.
ts.SyntaxKind
Each AST node has a kind attribute corresponding to ts.SyntaxKind, which marks the type of the current node. All categories can be viewed by definition of ts.Syntaxkind.
One problem is that SyntaxKind classes are defined by their names, but most of the time it’s not clear what syntax they’re using.
Other than searching for SyntaxKind keywords, it’s all guesswork. The recommended TypeScript AST Viewer is a tool that can see the AST tree in real time after typing TypeScript code.
Create a Program
TypeScript does type checking during compilation, and type information is derived by combining multiple files. For example, when checking the type of file A, you need to know the types of variables in file B imported by file A. TypeScript uses the Program class to represent a compilation unit. A Program is a collection of sourcefiles.
const program = ts.createProgram(fileNames, options);
Copy the code
FileNames represent input files, and TypeScript automatically parses import files and references to fileNames.
Get the SourceFile from Program
program.getSourceFiles();
Copy the code
For example, on the left we have an index.ts file, which imports the./test.ts file. On the right is how we used ts.CreateProgram, and you can see that we only passed in index.ts.
Print out all the sourcefiles and you can see that a large number of files have been introduced into Program, most of which are type definition files ending in.d.ts.
It also includes the test.ts file used by index.ts.
Use TypeChecker to get the AST node type
When we create a Program, we can use TypeChecker to check the type information of variables in our code.
let checker = program.getTypeChecker();
Copy the code
TypeChecker can answer the following questions:
- What is the Symbol for this Node?
- What is the type of this Symbol?
- Which symbols are visible in this part of the AST?
- What errors does this file have?
The TypeChecker instance is simple to use and provides a number of methods, such as:
const symbol = typeChecker.getSymbolAtLocation(node.name);
const nodeType = typeChecker.getTypeAtLocation(node);
Copy the code
Parse the TypeScript code and create the AST
If we want to create an AST from TypeScript code, we modify the AST to get TypeScript code without type checking. Instead of creating Program, create a SourceFile. A SourceFile can be created using the ts.CreatesourCefile method.
function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes? :boolean, scriptKind? : ScriptKind) :SourceFile;
Copy the code
If setParentNodes is set to true, each node will have information about its parent node, which is useful.
Modify the TypeScript AST code
Ts provides a series of create and update methods to create and modify the AST.
These methods are somewhat difficult to use:
- with
ts.Syntax
The types defined in the. - It’s mixed in with methods like createProgram, all in the TS namespace.
- No documentation was found.
We can pass the required parameters according to the type definition:
Outputs TypeScript code based on the AST
Printer can be used to produce TypeScript code based on AST, not JavaScript code.
const printer = ts.createPrinter();
printer.printNode(ts.EmitHint.SourceFile, sourceFile, sourceFile);
Copy the code
The second argument can be specified to a node in sourceFile, which directly outputs TypeScript code for that node.
printer.printNode(ts.EmitHint.SourceFile, sourceFile.statements[0].expression, sourceFile)
Copy the code
But the second argument cannot pass any node, only these types of nodes:
enum EmitHint {
SourceFile = 0,
Expression = 1,
IdentifierName = 2,
MappedTypeParameter = 3,
Unspecified = 4,
EmbeddedStatement = 5,
JsxAttributeValue = 6
}
Copy the code
Note that ts.sourcefile also has a sourcefile.getFullText () method, which returns only the TypeScript code that was created when the SourceFile was created, not the code that changed the AST.
That’s the simple way to use it. I’ll stop there today and fill in more details later.