The binder is the second major pass of the TypeScript compilation pipeline. After the parser produces an Abstract Syntax Tree (AST), the binder traverses it to create Symbol objects, populate SymbolTable maps, assign NodeFlags, establish lexical scopes, and build the control flow graph used for type narrowing. The binder does not perform type checking; that work belongs to the Type Checker (see 2.4). The AST itself is described in 2.2
Diagram: Binder in the Compilation Pipeline
The binder mutates the AST in-place: it does not produce a new data structure, but instead stamps Symbol references and FlowNode references onto existing AST nodes. The type checker then reads those annotations.
Sources: src/compiler/binder.ts1-20 src/compiler/checker.ts1-50 src/compiler/program.ts1-50
The binder exposes a single public function, bindSourceFile, which is called by the Program before type-checking is started.
Function Signature:
src/compiler/binder.ts47 (re-exported via src/compiler/checker.ts47)
Internally, bindSourceFile initializes binder state and then calls bind(file) to begin the recursive traversal. Key state variables include:
| Variable | Type | Purpose |
|---|---|---|
file | SourceFile | The source file being bound |
options | CompilerOptions | Compilation options (affects strictness, target version) |
parent | Node | Current parent node in the traversal |
container | Node | Current function/module/source file container |
blockScopeContainer | Node | Nearest block-scoped container for let/const |
currentFlow | FlowNode | Current position in the control flow graph |
labelStack | LabeledStatement[] | Stack of labeled statements for break/continue |
inStrictMode | boolean | Whether strict mode is active |
The function bind(node: Node | undefined) is the core recursive visitor that dispatches to specialized bind* functions based on node.kind.
Sources: src/compiler/binder.ts1-100 src/compiler/types.ts200-300
Symbol InterfaceEach named entity in a TypeScript program (variable, function, class, interface, enum, module, etc.) is represented by a Symbol. The Symbol interface is defined in types.ts and has these key fields:
| Field | Type | Purpose |
|---|---|---|
flags | SymbolFlags | Bitmask describing what kind of entity this symbol is |
escapedName | __String | Escaped, canonical name used as a map key (e.g., "myVar") |
declarations | Declaration[] | All AST nodes that contribute to this symbol |
valueDeclaration | Declaration | The primary value-producing declaration (first or most specific) |
members | SymbolTable | Instance members (class properties, interface members, enum members) |
exports | SymbolTable | Exported names (module exports, namespace exports) |
parent | Symbol | Enclosing namespace/module symbol |
id | number | Unique integer assigned at creation |
Symbol creation and allocation:
createSymbol(flags: SymbolFlags, name: __String): Symbol src/compiler/binder.ts42objectAllocator to instantiate a new symbol object src/services/services.ts242Symbol identity:
symbol.id is unique across all symbols in a program.getSymbolId(symbol) to obtain a stable numeric identifier for hashing and caching src/compiler/utilities.ts212Sources: src/compiler/types.ts1000-1100 src/compiler/binder.ts1-50 src/compiler/utilities.ts200-220 src/services/services.ts200-250
SymbolFlagsSymbolFlags is a bitmask enum in types.ts that classifies what a symbol represents. Multiple flags can be set simultaneously (e.g., a class is both Class and Value).
| Flag | Meaning |
|---|---|
FunctionScopedVariable | var declaration |
BlockScopedVariable | let or const declaration |
Property | Object or class property |
EnumMember | Enum member |
Function | Function declaration |
Class | Class declaration |
Interface | Interface declaration |
TypeAlias | Type alias declaration |
Enum | Enum declaration |
Module | Namespace or module |
Alias | Import/export alias |
Sources: src/compiler/types.ts1-50
SymbolTableA SymbolTable is a map from escaped names to Symbols. The utility function createSymbolTable creates one src/compiler/binder.ts42
Three distinct symbol tables exist on scoped containers:
| Table | Where | Contents |
|---|---|---|
locals | HasLocals nodes | Names declared locally in the scope |
members | Classes, interfaces, enums | Instance members and enum members |
exports | Modules, enums, namespaces | Exported names |
Sources: src/compiler/binder.ts25-26 src/compiler/utilities.ts118
The core binder function for registering a declaration handles merging logic.
node (identifier, literal, etc.) src/compiler/binder.ts105-106SymbolTable.createSymbol and insert into the table.let and const with same name), emit a diagnostic from diagnosticMessages.json src/compiler/diagnosticMessages.json6-12symbol.flags and append node to symbol.declarations.Sources: src/compiler/binder.ts1-100 src/compiler/types.ts1-100
Diagram: Container and Scope Relationships in the Binder
Sources: src/compiler/binder.ts145 src/compiler/utilities.ts173
ContainerFlagsThe binder uses ContainerFlags (accessed via getContainerFlags) to decide how to treat each AST node src/compiler/utilities.ts173:
| Flag | Meaning |
|---|---|
IsContainer | Creates a new lexical scope |
IsBlockScopedContainer | Can hold let/const variables src/compiler/binder.ts140 |
IsFunctionLike | Function body; hoists var declarations |
HasLocals | Node has a locals property src/compiler/binder.ts118 |
var declarations are hoisted to the enclosing function-scoped container. let/const/using declarations (identified by NodeFlags) are placed in the nearest block-scoped container src/compiler/binder.ts132-140
Sources: src/compiler/types.ts278 src/compiler/binder.ts220-224
The binder constructs a per-function control flow graph (CFG) using FlowNode objects. Each node in the CFG is linked via antecedent pointers.
Diagram: FlowNode Types and Their Relationships
Sources: src/compiler/types.ts221 src/compiler/binder.ts72-76
FlowFlagsFlowFlags is a bitmask on each FlowNode src/compiler/binder.ts72:
| Flag | Node type | Created when |
|---|---|---|
Start | FlowStart | Beginning of every function or source file |
BranchLabel | FlowLabel | Join point after if/try |
LoopLabel | FlowLabel | Loop entry |
Assignment | FlowAssignment | Variable assignment src/compiler/binder.ts69 |
TrueCondition | FlowCondition | Branch taken when condition is truthy src/compiler/binder.ts71 |
Sources: src/compiler/binder.ts68-76
The binder contains specialized bind* functions for every statement kind that affects control flow:
| Construct | Binder function |
|---|---|
if statement | bindIfStatement src/compiler/binder.ts123 |
while loop | bindWhileStatement |
for loop | bindForStatement src/compiler/binder.ts80 |
return | bindReturnOrThrow |
break/continue | bindBreakOrContinueStatement src/compiler/binder.ts21 |
Each AST node that "participates" in control flow has a flowNode property set src/compiler/binder.ts24
Sources: src/compiler/binder.ts1-100 src/compiler/utilities.ts216
NodeFlags Set by the BinderSeveral NodeFlags values are assigned during binding:
| Flag | Set on | Condition |
|---|---|---|
ReachabilityCheckFlags | FunctionLikeDeclaration | Used for implicit return checks |
GlobalAugmentation | ModuleDeclaration | declare global {} blocks src/compiler/binder.ts166 |
Sources: src/compiler/types.ts278 src/compiler/binder.ts160-170
AssignmentDeclarationKindIn .js files, the binder recognizes CommonJS and prototype patterns via AssignmentDeclarationKind src/compiler/binder.ts10
| Kind | Pattern |
|---|---|
ExportsProperty | exports.foo = ... |
ModuleExports | module.exports = ... src/compiler/binder.ts186 |
PrototypeProperty | Foo.prototype.bar = ... src/compiler/binder.ts208 |
Sources: src/compiler/binder.ts87-88 src/compiler/utilities.ts25
Diagram: Key Functions in binder.ts and Their Roles
The bind function uses a switch on node.kind to dispatch to specialized functions src/compiler/binder.ts77-83
Sources: src/compiler/binder.ts1-100
| Consumer | What it uses from the binder |
|---|---|
| Type Checker | Symbol, SymbolTable, FlowNode src/compiler/checker.ts1-50 |
| Emitter | ModuleInstanceState src/compiler/emitter.ts1-50 |
| Program | Calls bindSourceFile src/compiler/program.ts213 |
The type checker calls bindSourceFile via maybeBind before analysis src/compiler/program.ts213
Sources: src/compiler/checker.ts1-50 src/compiler/program.ts1-50 src/compiler/binder.ts1-50
Refresh this wiki