This page describes the multi-stage pipeline that transforms TypeScript source text into emitted JavaScript, declaration files, and source maps. It introduces the five core compiler components — scanner, parser, binder, type checker, and emitter — and explains how the Program object orchestrates them.
For detailed coverage of each stage, see the child pages:
For the transformer pipeline that runs between type checking and emit, see Transformation Pipeline. For language service features built on top of the compiler, see Language Service.
The compiler processes each TypeScript file through five sequential stages. Each stage consumes the output of the previous and adds more structure.
Compiler pipeline — source to output:
Sources: src/compiler/scanner.ts src/compiler/parser.ts src/compiler/binder.ts src/compiler/checker.ts src/compiler/emitter.ts
Program is the root compilation unit. It is created by calling createProgram() in src/compiler/program.ts and is the object callers use to access source files, the type checker, diagnostics, and emit results.
Program is defined in src/compiler/types.ts as an interface with methods including:
getSourceFiles() — all parsed SourceFile objects in the compilationgetCompilerOptions() — resolved CompilerOptionsgetTypeChecker() — lazily creates and returns the single TypeChecker instanceemit() — triggers transformation and emit via emitFiles() in emitter.tsgetSyntacticDiagnostics(), getSemanticDiagnostics(), getDeclarationDiagnostics()The CompilerHost interface abstracts file I/O. createProgram() accepts a CompilerHost (or constructs a default one) that supplies file reads, canonical file name resolution, and default library paths.
Program component relationships:
Sources: src/compiler/program.ts src/compiler/checker.ts src/compiler/binder.ts src/compiler/emitter.ts src/compiler/types.ts
The compiler operates on a small set of data structures that are passed between stages.
Every syntactic construct is a Node. The Node interface in src/compiler/types.ts has these essential fields:
| Field | Type | Set by | Description |
|---|---|---|---|
kind | SyntaxKind | Parser | Identifies the node type |
pos | number | Parser | Start offset in source (includes leading trivia) |
end | number | Parser | End offset in source |
flags | NodeFlags | Parser/Binder | Parsing context and binding annotations |
parent | Node | Binder | Link to parent node in the AST |
SourceFile is the root Node for a single file. It extends Node with the raw source text string, an array of top-level Statement nodes, file-level diagnostics, and metadata like languageVariant and scriptKind.
SyntaxKind is a large const enum defined at src/compiler/types.ts40-492 It covers all tokens, literals, punctuation, expressions, statements, declarations, JSX constructs, and JSDoc nodes. NodeFlags at src/compiler/types.ts782-848 encodes contextual information such as Let, Const, Ambient, JavaScriptFile, Synthesized, HasImplicitReturn, and HasExplicitReturn.
The binder creates Symbol objects for each declaration. A Symbol may have multiple declarations (declaration merging). Key fields on Symbol:
| Field | Type | Description |
|---|---|---|
flags | SymbolFlags | What kinds of entity this symbol represents |
escapedName | __String | Mangled identifier name |
declarations | Declaration[] | All AST nodes that declare this symbol |
valueDeclaration | Declaration | Primary value-space declaration |
members | SymbolTable | Member symbols (for classes, interfaces, enums) |
exports | SymbolTable | Exported members |
SymbolTable is Map<__String, Symbol>. SymbolFlags is a bitfield with values including FunctionScopedVariable, BlockScopedVariable, Class, Interface, Enum, Module, TypeAlias, TypeParameter, Alias, and many others.
The type checker produces Type objects. Every Type carries a TypeFlags bitfield that identifies the type category. Key concrete subtypes include:
| Subtype | TypeFlags bits | Examples |
|---|---|---|
IntrinsicType | Any, Unknown, String, Number, Boolean, Void, Undefined, Null, Never | any, string, never |
LiteralType | StringLiteral, NumberLiteral, BigIntLiteral, BooleanLiteral | "hello", 42, true |
UnionType | Union | string | number |
IntersectionType | Intersection | A & B |
TypeReference | Object | Array<T>, Promise<string> |
InterfaceType | Object | interfaces, classes |
TypeParameter | TypeParameter | T in function f<T> |
ConditionalType | Conditional | T extends U ? X : Y |
IndexedAccessType | IndexedAccess | T[K] |
All type and interface definitions live in src/compiler/types.ts
Core type relationships — bridging AST to semantic layer:
Sources: src/compiler/types.ts src/compiler/binder.ts src/compiler/checker.ts
File: src/compiler/scanner.ts
The scanner converts raw source text into a sequential stream of SyntaxKind tokens. It is created by createScanner() and is a stateful cursor that the parser advances by repeatedly calling scan().
The scanner handles:
#name)JSDocParsingMode)#!) lines, and conflict marker triviaTokenFlags to record numeric literal formats (hex, octal, binary), template literal kinds, and whether a string was unterminatedThe parser does not own the scanner; a single shared scanner instance is reused.
Sources: src/compiler/scanner.ts
File: src/compiler/parser.ts
The parser drives the scanner to construct a full AST. Node creation uses parseNodeFactory (src/compiler/parser.ts441) which wraps parseBaseNodeFactory (src/compiler/parser.ts432-438). The base factory allocates concrete node objects using pluggable constructors from objectAllocator, allowing the language service to substitute richer node implementations.
The parser sets pos and end on every node directly from scanner positions, and also records parse errors directly on the SourceFile diagnostics array.
Key capabilities:
tryParse() / lookAhead() to resolve ambiguous grammar points without consuming tokensupdateSourceFile() accepts a TextChangeRange and reuses unchanged subtrees from a prior SourceFileLanguageVariant.JSXJSDocParsingMode (for performance, JSDoc can be skipped or parsed lazily)Sources: src/compiler/parser.ts432-441 src/compiler/parser.ts
File: src/compiler/binder.ts
The binder performs a single pass over the AST to produce the symbol and control flow information needed by the type checker. It is invoked per-file via bindSourceFile(), which is called lazily by the type checker on first access.
The binder:
Symbol is created (or merged into an existing one for declaration merging). Symbols are placed into the appropriate SymbolTable: the file's locals, a class's members, a module's exports, etc.node.parent is assigned for every node in the tree.NodeFlags — flags like HasImplicitReturn, HasExplicitReturn, HasAsyncFunctions, Ambient, and Unreachable are set on function and source file nodes.FlowNode objects are created and linked together using FlowFlags values (Assignment, Condition, Call, ArrayMutation, SwitchClause, Label, ReduceLabel, Start, BranchLabel, LoopLabel). Each relevant AST node gets a flowNode property pointing into this graph.Sources: src/compiler/binder.ts
File: src/compiler/checker.ts
The type checker is the largest component in the compiler. It is created once per Program by createTypeChecker() and returned by Program.getTypeChecker().
The type checker's responsibilities:
SymbolFlowNode chains and TypeFacts (src/compiler/checker.ts1230-1259)Diagnostic objects for type errors, using messages from src/compiler/diagnosticMessages.jsonEmitResolver — exposes a restricted interface queried by the emitter for emit-relevant semantic facts (e.g., is a declaration visible, is an import type-only, what is an enum member's constant value)Results are cached in two side tables:
NodeLinks — per-node cache keyed by node.id, storing resolved types, symbol references, and check flagsSymbolLinks — per-symbol cache keyed by symbol.id, storing resolved types, inferred types, and mapper stateThe checker is entirely lazy: nothing is computed until a caller explicitly requests it.
Sources: src/compiler/checker.ts1230-1259 src/compiler/checker.ts
Files: src/compiler/emitter.ts src/compiler/transformer.ts (various)
Emit is triggered by Program.emit(), which calls emitFiles() in src/compiler/emitter.ts For each source file the emitter:
enum, namespace, parameter properties, decorators) and modern ECMAScript features to the target language version. Transformers use a visitor pattern over the AST and produce a new transformed tree without mutating the original.Printer, which serializes it to text. The Printer uses EmitHint to know the grammatical context of a node (expression vs. statement vs. identifier, etc.) and EmitFlags to control formatting decisions like skipping trailing semicolons or forcing a single line.Printer emits SourceMapEmitResult entries that are collected by a SourceMapGenerator..d.ts output.forEachEmittedFile() at src/compiler/emitter.ts446-479 handles the logic of iterating output files (including the case of outFile bundling multiple inputs into one output).
Sources: src/compiler/emitter.ts446-479 src/compiler/emitter.ts
The compiler is designed around lazy, demand-driven computation:
| What | When it runs |
|---|---|
Parsing (createSourceFile) | On first read of a file |
Binding (bindSourceFile) | On first semantic query for a file |
| Type checking (per node/symbol) | On first access of that node's/symbol's type |
| Emit | Only when explicitly requested |
This design allows the language service to hold a long-lived Program across edits, reuse unchanged SourceFile objects (incremental parsing), and avoid redundant work when only a small part of the program changes.
The StructureIsReused enum in program.ts tracks whether an updated Program can reuse its prior structure (Completely, SafeModules, or Not).
Each compilation stage produces Diagnostic objects. All diagnostic message strings and codes are centrally defined in src/compiler/diagnosticMessages.json Each entry has a category and a numeric code:
| Category | Numeric Range | Stage |
|---|---|---|
| Syntactic errors | 1000–1999 | Scanner / Parser |
| Semantic errors | 2000–2999 | Type Checker |
| Declaration emit errors | 4000–4999 | Emitter |
| Option / config errors | 5000–6999 | Program / commandLineParser |
The build system generates typed constants from this JSON file into the Diagnostics namespace, so the compiler can reference messages by name rather than by raw string.
Sources: src/compiler/diagnosticMessages.json src/compiler/types.ts
| File | Role |
|---|---|
src/compiler/types.ts | All core interfaces and enums (Node, SourceFile, Symbol, Type, SyntaxKind, NodeFlags, etc.) |
src/compiler/scanner.ts | Tokenizer — createScanner() |
src/compiler/parser.ts | AST builder — parseSourceFile(), incremental parsing |
src/compiler/binder.ts | Symbol/scope/flow-graph builder — bindSourceFile() |
src/compiler/checker.ts | Type inference and checking — createTypeChecker() |
src/compiler/emitter.ts | Output writer — emitFiles(), Printer |
src/compiler/program.ts | Compilation orchestration — createProgram() |
src/compiler/utilities.ts | Shared AST utility functions |
src/compiler/core.ts | Fundamental collection and algorithm utilities |
src/compiler/commandLineParser.ts | CompilerOptions and tsconfig.json parsing |
src/compiler/diagnosticMessages.json | Source of all diagnostic message text and codes |
src/compiler/sys.ts | Default System implementation (file I/O, watching) |
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.