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:

  1. 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.
  2. Binding: The Binder links symbols across scopes, creating a map of variable declarations, interfaces, and function references.
  3. 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.
  4. 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 .js and 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.