25.7 Building a Simple Code Generator

Right, so you want to build a code generator. Not just any code generator, but one that understands the structure of the code it’s manipulating. You’re not just concatenating strings like a barbarian; you’re a sculptor, and the TypeScript Compiler API is your chisel. It’s the difference between sending a mass email and writing a personal letter. The former might get the job done, but the latter is correct, robust, and doesn’t accidentally call the recipient by the wrong name.

25.6 ts-morph: A Higher-Level API for AST Manipulation

Alright, let’s get our hands dirty. You’ve met the raw TypeScript Compiler API. It’s powerful, but let’s be honest, it feels like you’re trying to perform open-heart surgery with a rusty spoon while wearing oven mitts. The API is low-level, verbose, and requires you to constantly check the node.flags bitmask to figure out what you’re even looking at. It’s a masterpiece of engineering, but a nightmare of ergonomics. This is where ts-morph swoops in like a superhero in a nicely tailored suit. It’s a library that wraps the raw Compiler API, giving you all its power but with a sane, object-oriented, and downright pleasant interface. Instead of dealing with ts.SyntaxKind.SomeObscureEnum and ts.isCallExpression(node), you work with clear classes like CallExpression. It’s the difference between assembling a car from a pile of parts and simply turning the key.

25.5 Writing a Custom Transformer

Right, so you want to mess with the very fabric of your code as it’s being compiled. Not content with just writing TypeScript, you want to reach into the compiler’s guts and twist the knobs. I respect that. It’s how you build the next generation of linters, code formatters, and those fancy tools that feel like magic (until you have to debug them). We call this a “Custom Transformer,” and it’s your VIP pass to the Abstract Syntax Tree (AST) party. The AST is the compiler’s internal, object-oriented representation of your code. A transformer’s job is to walk through this tree, find the nodes it cares about, and then optionally replace, update, or delete them to produce a new, transformed tree. It’s like performing surgery on your code while it’s still just a thought in the compiler’s brain.

25.4 Extracting Type Information from the Type Checker

Alright, let’s get our hands dirty. You’ve got a ts.Program and its trusty sidekick, the Type Checker (ts.createTypeChecker()). This isn’t some glorified linter; this is the engine room of the entire language service. It’s the thing that actually knows what string is, why your generic is failing, and that the property you’re trying to access on that object definitely, probably, doesn’t exist. Its job is to take all those abstract syntax trees and turn them into a coherent web of types.

25.3 Walking the AST: ts.Node, ts.SyntaxKind

Alright, let’s get our hands dirty. You’ve got a TypeScript program in memory, parsed into an Abstract Syntax Tree (AST). It’s a beautiful, terrifying, and deeply nested structure of objects. Your job is to traverse it, find the bits you care about, and do something useful. This isn’t about reading the file as text; it’s about understanding its meaning programmatically. To do that, you need to know two things intimately: ts.Node and ts.SyntaxKind.

25.2 Creating a Program and Type Checker

Right, let’s get our hands dirty. You’ve parsed a file, you’ve looked at its AST, and you feel like a wizard. But a single file is a lonely island. In the real world, TypeScript understands your code by seeing the whole archipelago—every file, every dependency, every declaration. That holistic view is encapsulated in a ts.Program. This isn’t just a fancy concept; it’s the beating heart of the compiler API. It’s the in-memory representation of your entire project, and without it, you’re just playing with syntax trees in a vacuum.

25.1 Why Use the Compiler API: Linters, Codemods, Generators

Look, you don’t reach for the TypeScript Compiler API because you had a nice, normal day and thought, “You know what sounds relaxing?” You reach for it when you have a problem that can’t be solved by just writing more TypeScript. It’s the power tool for when you need to not just use the language, but understand it, manipulate it, and generate it programmatically. Think of it as the difference between driving a car and being a mechanic with a full diagnostic computer. Most of us just need to drive. But when you need to tune the engine or, heaven forbid, build a new car from scratch, you need the mechanic’s tools.

— joke —

...