How TypeScript Compiles to Standard JavaScript
This article explains how the TypeScript compiler (tsc)
processes, transforms, and outputs TypeScript source code into standard,
browser-compatible JavaScript. It breaks down the internal compilation
pipeline step by step, covering lexical analysis, Abstract Syntax Tree
(AST) creation, type checking, code downleveling, and final file
emission.
The TypeScript Compilation Pipeline
The TypeScript compiler transforms source files into standard JavaScript through a multi-stage architecture:
Source Code (.ts)
→ Scanner (Tokens)
→ Parser (AST)
→ Binder (Symbols)
→ Checker (Type Validation)
→ Emitter (JavaScript Code)
1. Scanning and Parsing (Creating the AST)
The process begins with the Scanner, which reads the
raw .ts text and breaks it into individual tokens (such as
keywords, identifiers, operators, and literals).
Next, the Parser consumes these tokens to construct an Abstract Syntax Tree (AST). The AST is a deeply nested tree structure that represents the syntactic hierarchy and relationships of the code. Both the standard JavaScript syntax and TypeScript-specific constructs (interfaces, type annotations, generics) are represented as nodes within this tree.
2. Binding and Type Checking
Once the AST is built:
- The Binder: Traverses the AST to create “Symbols.” Symbols link declarations (such as variable or function names) across different scopes, mapping identifiers to their declarations.
- The Type Checker: Analyzes the AST and Symbol table to enforce TypeScript’s type system rules. It verifies assignments, function arguments, return types, and interface implementations.
By default, even if the Type Checker finds errors, the compiler will
still proceed to emit JavaScript unless the noEmitOnError
flag is set to true.
3. Transformation and Type Erasure
The primary responsibility of the Emitter is to convert the TypeScript AST into an output-ready JavaScript AST. This stage involves two main operations:
- Complete Type Erasure: TypeScript types exist solely at compile time. The emitter strips away all type annotations, interface declarations, type aliases, and abstract class signatures without leaving any runtime footprint.
- TypeScript-Specific Syntactic Lowering: A few TypeScript features generate runtime code rather than being completely erased. Enums, namespaces, and parameter properties in class constructors are transformed into standard JavaScript objects, functions, and assignment statements.
4. Downleveling JavaScript Features
The compiler can target various ECMAScript versions (such as
ES5, ES2015, ES2020, or
ESNext), configured via the target option in
tsconfig.json.
When compiling to an older target (e.g., ES5): * Modern
syntax like arrow functions, async/await, class
definitions, and optional chaining are converted into compatible
ECMAScript 5 constructs (such as standard function declarations,
prototype assignments, and helper functions). * Modern module syntax
(import/export) can be converted to CommonJS
(require/module.exports), AMD, or UMD formats
based on the module compiler setting.
5. Code Generation and Emission
In the final step, the printer converts the transformed AST back into plain text. The compiler writes the following output files to disk:
.jsFiles: The resulting standard JavaScript code containing only valid ECMAScript syntax ready to be executed by any standard runtime (such as Node.js, Deno, or web browsers)..d.tsDeclaration Files (Optional): When thedeclarationflag is enabled, the emitter generates type definition files containing only the exported types and signatures for external consumers..js.mapSource Maps (Optional): When thesourceMapflag is enabled, mapping files are emitted to allow debuggers to map runtime execution in the JavaScript file back to the original TypeScript source code lines.