How Does TypeScript Transpile to JavaScript?
This article breaks down how the TypeScript compiler transforms statically typed code into vanilla JavaScript, focusing on the syntax analysis, semantic type checking, and the complete erasure of static types during the emit phase.
The Role of Type Erasure
TypeScript is designed around the principle of structural type
erasure. During compilation, the TypeScript compiler (tsc)
processes and checks every type annotation against its type rules, but
it does not emit runtime equivalents of those types. The types exist
solely at build time to catch bugs, ensure contracts, and power
developer tooling. Once the validation succeeds, the compiler strips out
interfaces, type aliases, generic signatures, and explicit type
declarations entirely.
For example, a function written in TypeScript:
function calculateTotal(price: number, tax: number): number {
return price + (price * tax);
}Transpiles directly into standard JavaScript with the annotations removed:
function calculateTotal(price, tax) {
return price + (price * tax);
}The resulting JavaScript engine sees only raw JavaScript values with dynamic runtime semantics.
The Compilation Pipeline
The process of converting a .ts file into executable
.js follows a multi-stage compilation pipeline managed by
internal compiler modules:
- Parsing and AST Creation: The Scanner reads source code into tokens, and the Parser organizes these tokens into an Abstract Syntax Tree (AST), representing both structural code constructs and type nodes.
- Binding: The Binder links symbols across scopes, creating a map of variable declarations, interfaces, and function references.
- Type Checking: The Checker traverses the bound AST. It verifies assignability, checks interfaces, runs type inference, and catches compiler errors. This step produces diagnostics but does not emit code.
- Transformation and Emit: The Emitter visits the AST
nodes, executes downlevel transformations (such as converting modern ES
features into ES5 or CommonJS, if configured), drops all type nodes, and
writes pure
.jsand optional source map files to disk.
Runtime Exceptions: Enums and Namespaces
While most TypeScript constructs vanish, a few legacy TypeScript features generate executable JavaScript code:
- Enums: Standard numeric and string enums do not simply disappear; the compiler outputs a bidirectional or mapped JavaScript object closure so enum values can be read at runtime.
- Namespaces: TypeScript namespaces transpile to immediately invoked function expressions (IIFEs) attached to a global or scoped object.
- Class Parameter Properties: Adding access modifiers
directly to constructor parameters (such as
constructor(public id: string)) triggers the compiler to inject assignment statements (this.id = id;) inside the constructor body.
Tools like Babel or swc skip semantic type checking
entirely and solely transpile by running regular expressions or AST
transforms to strip types. In contrast, tsc can validate,
transform modern syntax down to older ECMAScript targets, and erase
static typing in one coherent pass.