2 min read

TypeScript Compiler API Basics

The TypeScript Compiler API allows you to interact with the TypeScript compiler programmatically. You can use it to parse code into an Abstract Syntax Tree (AST), inspect types, transform code, and generate output. This is the foundation for tools like linters, formatters, and custom transformers.

1. Installation

You just need the standard typescript package.

npm install typescript
npm install --save-dev @types/node

2. Parsing Code (Creating an AST)

To analyze code, you first need to parse it into a SourceFile.

import * as ts from "typescript";

const sourceCode = `
class Greeter {
    greet() {
        console.log("Hello, world!");
    }
}
`;

const sourceFile = ts.createSourceFile(
    "example.ts",   // File name (virtual)
    sourceCode,     // Source code string
    ts.ScriptTarget.Latest, // Language version
    true            // setParentNodes (useful for traversal)
);

3. Traversing the AST

Once you have a SourceFile, you can traverse it to inspect nodes. TypeScript provides ts.forEachChild to iterate over child nodes efficiently.

function printRecursive(node: ts.Node, indent: number = 0) {
    const indentation = "  ".repeat(indent);
    const syntaxKind = ts.SyntaxKind[node.kind];

    console.log(`${indentation}${syntaxKind}`);

    // Recurse into children
    ts.forEachChild(node, child => printRecursive(child, indent + 1));
}

printRecursive(sourceFile);

Output:

SourceFile
  ClassDeclaration
    Identifier
    MethodDeclaration
      Identifier
      Block
        ExpressionStatement
          CallExpression
            PropertyAccessExpression
              Identifier
              Identifier
            StringLiteral
    EndOfFileToken

4. Inspecting Nodes

You can cast nodes to specific interfaces to access their properties.

function analyzeNode(node: ts.Node) {
    if (ts.isClassDeclaration(node)) {
        // node is now typed as ts.ClassDeclaration
        if (node.name) {
            console.log(`Found class: ${node.name.text}`);
        }
    }

    ts.forEachChild(node, analyzeNode);
}

analyzeNode(sourceFile);

5. Creating/Printing Code

You can also generate code programmatically using factory functions and the Printer API.

const factory = ts.factory;

// Create: const x = 42;
const statement = factory.createVariableStatement(
    undefined,
    factory.createVariableDeclarationList(
        [
            factory.createVariableDeclaration(
                factory.createIdentifier("x"),
                undefined,
                undefined,
                factory.createNumericLiteral(42)
            )
        ],
        ts.NodeFlags.Const
    )
);

const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });

const result = printer.printNode(
    ts.EmitHint.Unspecified,
    statement,
    sourceFile // used for context (indentation, etc)
);

console.log(result); // const x = 42;

6. Type Checking (Advanced)

To inspect semantic information (types, symbols), you need to create a ts.Program.

const program = ts.createProgram(["example.ts"], {});
const checker = program.getTypeChecker();

// You would then traverse source files from program.getSourceFiles()
// and use checker.getTypeAtLocation(node)

programming/javascript/typescript/typescript