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.3). The AST itself is described in 2.1.
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.ts src/compiler/checker.ts src/compiler/program.ts
The binder exposes a single public function, bindSourceFile, which is called by the Program before type-checking is started.
bindSourceFile(file: SourceFile, options: CompilerOptions): void
Internally, bindSourceFile initializes binder state (current container, current flow node, etc.) and then calls bind(file) to begin the recursive traversal.
Sources: src/compiler/binder.ts1-310 src/compiler/checker.ts1-50
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 |
declarations | Declaration[] | All AST nodes that contribute to this symbol |
valueDeclaration | Declaration | The primary value-producing declaration |
members | SymbolTable | Instance members (class, interface, enum) |
exports | SymbolTable | Exported names (module, enum) |
parent | Symbol | Enclosing namespace/module symbol |
id | number | Unique integer assigned at creation |
mergeId | number | Used when symbols are merged across declaration sites |
The concrete implementation used at runtime is the object produced by objectAllocator.getSymbolConstructor(). Symbols are created via createSymbol inside the binder.
Sources: src/compiler/types.ts1-50 src/compiler/binder.ts1-310 src/services/services.ts656-699
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 Type 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 |
Transient | Synthetic symbol created by the checker |
Sources: src/compiler/types.ts1-50
SymbolTableA SymbolTable is simply Map<__String, Symbol>. The utility function createSymbolTable (in utilities.ts) creates one, optionally pre-populated from an array of symbols.
src/compiler/utilities.ts644-652
Three distinct symbol tables exist on scoped containers:
| Table | Where | Contents |
|---|---|---|
locals | HasLocals nodes (functions, source files, blocks) | Names declared locally in the scope |
members | Classes, interfaces, enums | Instance members and enum members |
exports | Modules, enums, namespaces | Exported names |
declareSymbol(symbolTable, parent, node, includes, excludes) is the core binder function for registering a declaration. Its logic:
symbolTable.includes, add this declaration to symbol.declarations.excludes, emit a duplicate-identifier diagnostic.symbol.valueDeclaration to the most recent value-producing declaration.This merging is how TypeScript allows interface augmentation, function overloads, and namespace merging across multiple declaration sites.
Sources: src/compiler/binder.ts1-310
Diagram: Container and Scope Relationships in the Binder
Sources: src/compiler/binder.ts src/compiler/utilities.ts
ContainerFlagsThe binder uses an internal ContainerFlags bitmask (defined in utilities.ts and accessed via getContainerFlags(node)) to decide how to treat each AST node as the traversal descends:
| Flag | Meaning |
|---|---|
IsContainer | Creates a new lexical scope (own locals symbol table) |
IsBlockScopedContainer | Can hold let/const variables |
IsFunctionLike | Function body; hoists var and function declarations to this level |
IsInterface | Interface body; members go into members table |
HasLocals | Node has a locals property to receive local symbols |
When the binder enters a container node, it saves the current container on a stack (parent, container, blockScopeContainer) and restores them on exit. This stack models lexical nesting and determines which symbol table receives each declaration.
Sources: src/compiler/utilities.ts src/compiler/binder.ts
var declarations inside functions are hoisted to the enclosing function-scoped container. let/const/using declarations (identified by NodeFlags.Let, NodeFlags.Const, NodeFlags.Using) are placed in the nearest block-scoped container. The flags SymbolFlags.FunctionScopedVariable vs. SymbolFlags.BlockScopedVariable distinguish these cases.
Sources: src/compiler/types.ts782-848 src/compiler/binder.ts
The binder constructs a per-function control flow graph (CFG) using FlowNode objects. Each node in the CFG is linked via antecedent or antecedents pointers back toward the function entry. The type checker reads this graph during narrowing.
Diagram: FlowNode Types and Their Relationships
Sources: src/compiler/types.ts src/compiler/binder.ts
FlowFlagsFlowFlags is a bitmask on each FlowNode:
| Flag | Node type | Created when |
|---|---|---|
Unreachable | — | Code after return, throw, break, continue |
Start | FlowStart | Beginning of every function or source file |
BranchLabel | FlowLabel | Join point after if/try/conditional |
LoopLabel | FlowLabel | Loop entry (back-edge target) |
Assignment | FlowAssignment | Variable assignment |
TrueCondition | FlowCondition | Branch taken when condition is truthy |
FalseCondition | FlowCondition | Branch taken when condition is falsy |
SwitchClause | FlowSwitchClause | Entry to a switch case |
ArrayMutation | FlowArrayMutation | push/unshift on array |
Call | FlowCall | Call to assertion function |
ReduceLabel | FlowReduceLabel | Post-loop narrowing |
Sources: src/compiler/types.ts src/compiler/binder.ts
The binder contains specialized bind* functions for every statement kind that affects control flow:
| Construct | Functions involved |
|---|---|
if statement | Creates FlowCondition (true branch) + FlowCondition (false branch) + FlowLabel (join) |
while/for/do | Creates FlowLoopLabel (pre-loop) + back edge |
return/throw | Ends current flow with FlowFlags.Unreachable after joining to exit label |
break/continue | Jumps to the nearest enclosing labeled statement's flow label |
try/catch/finally | Forks flow into try body, catch clause, and finally |
Assignment x = e | Emits FlowAssignment node referencing the target |
Logical &&/` | |
switch | Emits FlowSwitchClause per case |
Each AST node that "participates" in control flow has a flowNode property set by the binder. The predicate canHaveFlowNode(node) determines which node kinds are eligible.
Sources: src/compiler/binder.ts src/compiler/types.ts
NodeFlags Set by the BinderSeveral NodeFlags values are not set by the parser and are instead assigned during binding:
| Flag | Set on | Condition |
|---|---|---|
ExportContext | Declaration nodes | Node is inside an exported context |
HasImplicitReturn | FunctionLikeDeclaration | A code path exits without a return |
HasExplicitReturn | FunctionLikeDeclaration | A reachable explicit return exists |
GlobalAugmentation | ModuleDeclaration | Module is a declare global {} block |
HasAsyncFunctions | SourceFile | File contains at least one async function |
Unreachable | Statements | Statement follows a return/throw/break |
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn is a convenience alias used by the type checker.
Sources: src/compiler/types.ts782-848 src/compiler/binder.ts
AssignmentDeclarationKindWhen binding .js files (identified by NodeFlags.JavaScriptFile or isInJSFile), the binder recognizes CommonJS and prototype-based patterns as implicit declarations. The AssignmentDeclarationKind enum categorizes these:
| Kind | Pattern |
|---|---|
ExportsProperty | exports.foo = ... |
ModuleExports | module.exports = ... |
PrototypeProperty | Foo.prototype.bar = ... |
ThisProperty | this.x = ... inside constructor |
Property | Foo.bar = ... static expando |
ObjectDefinePropertyValue | Object.defineProperty(obj, 'key', ...) |
The function getAssignmentDeclarationKind classifies a binary expression and the binder uses the result to call the appropriate bindSpecialPropertyDeclaration variant.
Sources: src/compiler/binder.ts src/compiler/utilities.ts
For namespace/module declarations, the binder computes ModuleInstanceState to determine whether the namespace produces any runtime value:
| State | Meaning |
|---|---|
NonInstantiated | Only type declarations inside; no JS emitted |
Instantiated | Contains at least one value-producing declaration |
ConstEnumOnly | Contains only const enum declarations |
This is used by the emitter to skip empty module wrappers.
Sources: src/compiler/binder.ts src/compiler/utilities.ts
The binder emits its own category of diagnostics, separate from parse-time and type-check-time errors. These are collected into file.bindDiagnostics. Typical binder diagnostics include:
return outside a function bodybreak/continue without an enclosing loop or switchdefault labels in switchlet/const identifiers before their blockThe function canIncludeBindAndCheckDiagnostics determines whether a file's bind diagnostics should be surfaced to the user (excluded for declaration files in certain contexts).
Sources: src/compiler/binder.ts src/compiler/diagnosticMessages.json
Diagram: Key Functions in binder.ts and Their Roles
Sources: src/compiler/binder.ts
| Consumer | What it uses from the binder |
|---|---|
Type Checker (checker.ts) | Symbol, SymbolTable, FlowNode, NodeFlags.HasImplicitReturn |
Emitter (emitter.ts) | ModuleInstanceState, NodeFlags.HasAsyncFunctions |
| Language Service | Symbol resolution via checker, which depends on binder output |
Program (program.ts) | Calls bindSourceFile; collects file.bindDiagnostics |
The type checker calls bindSourceFile lazily (the first time it needs to examine a file) via the getSymbolOfNode and related paths. After binding, the checker uses getFlowTypeOfReference and related functions that walk the FlowNode graph to implement narrowing.
Sources: src/compiler/checker.ts1-50 src/compiler/program.ts src/compiler/binder.ts
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.