Lab 04: TypeScript Compiler API

Time: 40 minutes | Level: Advanced | Docker: docker run -it --rm node:20-alpine sh

Use the TypeScript Compiler API to parse, analyze, and transform TypeScript source code programmatically. Then simplify with ts-morph.


Step 1: Environment Setup

docker run -it --rm node:20-alpine sh
npm install -g typescript
mkdir lab04 && cd lab04
npm init -y
npm install typescript ts-morph

💡 We use node (not ts-node) here since the Compiler API is a JS library. ts-morph wraps it for a friendlier API.


Step 2: Creating a TypeScript Program

The ts.createProgram function is the entry point to TypeScript's compiler:

// explore-ast.js
const ts = require('typescript');
const fs = require('fs');

// Write a sample TS file to analyze
const sampleCode = `
function greet(name: string): string {
  return "Hello, " + name;
}

async function fetchUser(id: number): Promise<string> {
  return "user-" + id;
}

const arrowFn = (x: number, y: number): number => x + y;

class UserService {
  constructor(private name: string) {}
  getUser(): string { return this.name; }
}
`;

fs.writeFileSync('sample.ts', sampleCode);

// Create compiler host and program
const program = ts.createProgram(['sample.ts'], {
  target: ts.ScriptTarget.ES2020,
  module: ts.ModuleKind.CommonJS,
  strict: true,
});

const sourceFile = program.getSourceFile('sample.ts');
console.log('Source file:', sourceFile?.fileName);
console.log('Language version:', sourceFile?.languageVersion);
console.log('Statements count:', sourceFile?.statements.length);

Run:


Step 3: Understanding SyntaxKind

ts.SyntaxKind is an enum with 350+ values identifying every AST node type:

💡 You can check a node's kind with node.kind === ts.SyntaxKind.FunctionDeclaration or use TypeScript's type guard helpers like ts.isFunctionDeclaration(node).


Step 4: AST Traversal with forEachChild

Traverse every node in the AST:


Step 5: Finding All Function Declarations

A practical use case — extract all function signatures:


Step 6: Type Checking with the Type Checker

Access inferred types programmatically:


Step 7: Simpler Access with ts-morph

ts-morph wraps the Compiler API with a clean, ergonomic interface:

📸 Verified Output:


Step 8: Capstone — Code Analysis Tool

Build a complete TypeScript code analyzer:

Run:


Summary

Concept
API
Use Case

Parse source

ts.createProgram / ts.createSourceFile

Load TS files

Traverse AST

ts.forEachChild(node, visitor)

Walk all nodes

Identify nodes

ts.SyntaxKind enum / type guards

isFunctionDeclaration(n)

Type information

program.getTypeChecker()

Get inferred types

High-level API

ts-morph Project/SourceFile

Ergonomic analysis

Diagnostics

project.getPreEmitDiagnostics()

Find type errors

Last updated