TypeScript June 26, 2026 Aditya Rawas

How TypeScript Compiles to JavaScript

In Part 1, we explored why TypeScript exists and the problems it solves. Now comes the critical follow-up question every developer eventually asks:

If browsers and Node.js only understand JavaScript — how does TypeScript actually run?

The answer is a process called compilation. Every .ts file you write must be transformed into plain .js before it can execute anywhere. This guide walks you through that transformation — step by step, with interactive demos so you can see exactly what happens.


Why TypeScript Cannot Run Directly

JavaScript engines — Chrome’s V8, Firefox’s SpiderMonkey, Node.js — are built to execute JavaScript. TypeScript adds syntax that doesn’t exist in JavaScript:

  • Type annotations: const name: string = "John"
  • Interfaces: interface User { name: string }
  • Enums, access modifiers, generics

When a JavaScript engine encounters : string after a variable name, it doesn’t know what to do with it. It throws a syntax error and stops. TypeScript code is simply not valid JavaScript.

The solution: a TypeScript compiler reads your .ts files, strips all the TypeScript-specific syntax, and outputs valid .js files. That compiled JavaScript is what actually runs.


What TypeScript Compilation Actually Does

Before getting into the mechanics, let’s see the transformation directly. Click Strip Types below and watch what the compiler does to your code:

Live Type Stripper

Watch TypeScript compile — click to strip type annotations

index.ts TypeScript
interface User {
name: string;
age: number;
}

const user: User = { name: "John", age: 30 };

function greet(user: User): string {
return user.name;
}
index.js waiting…
const user = { name: "John", age: 30 };

function greet(user) {
return user.name;
}
3 type annotations detected (interface block + 2 inline tokens)

Notice what happened: the entire interface block is gone, and every : User / : string annotation on variables and function parameters disappeared. The runtime logic — the object shape, the function body — is identical. TypeScript’s type information is compile-time only. It exists to help you write correct code; it has zero presence at runtime.

This property is called type erasure, and it’s a fundamental characteristic of how TypeScript works.


The 4-Step TypeScript Compilation Pipeline

Type erasure sounds simple, but the compiler can’t just run a “find and replace” on type syntax. It needs to fully understand your code to do so safely. That requires a pipeline of four distinct steps.

Step through each stage using the interactive visualizer below:

Compilation Pipeline

Step through the 4 stages of TypeScript compilation

STEP 1 / 5
Your TypeScript File — index.ts
interface User {
name: string;
age: number;
}

const user: User = { name: "John", age: 30 };
console.log(user.name);

The compiler reads your .ts source file. It sees both logic and type annotations.

Source Code

Let’s summarize what you just saw:

StepNameWhat Happens
1LexingSource code → stream of tokens (keywords, identifiers, symbols)
2ParsingToken stream → Abstract Syntax Tree (AST) representing code structure
3Type CheckingTypeScript walks the AST, validates every type constraint, reports errors
4Code GenerationWalks the AST again, skips TS-only nodes, emits plain JavaScript

The type checker is the most expensive step — it needs to understand your entire codebase. This is also why tsc can be slow on large projects: it’s doing real analysis, not just string manipulation.

At which step does the TypeScript compiler catch type errors like passing a number where a string is expected?

How TypeScript Runs in Node.js

Once you understand compilation, running TypeScript in Node.js is straightforward. There are two main approaches:

Approach 1: Compile, Then Run

This is the explicit two-step flow. You call tsc to compile your TypeScript to JavaScript, then run the compiled output with Node.

# Compile TypeScript → JavaScript
npx tsc src/index.ts --outDir dist

# Run the compiled JavaScript
node dist/index.js

See the difference in the code itself — toggle between what you write and what Node.js executes:

// dist/index.js — what NODE.JS runs
const user = { name: "John", age: 30 };
console.log(user.name);

The interface and type annotations are gone. The compiled dist/index.js is a first-class JavaScript file — lightweight, no TS overhead, runs as fast as any JS.

Approach 2: Compile In-Memory and Run Immediately

Tools like tsx and ts-node skip the file step entirely. They compile your TypeScript to JavaScript in memory and hand it directly to Node.js — no .js file is ever written to disk.

# tsx: fast, uses esbuild under the hood
npx tsx src/index.ts

# ts-node: slower, uses full tsc
npx ts-node src/index.ts

What happens under the hood:

src/index.ts
    ↓ [tsx compiles in RAM]
  JavaScript (never touches disk)
    ↓ [Node.js executes immediately]
  Output: John

Which to use?

  • Development: tsx — fast startup, great DX
  • Production: compile with tsc or esbuild, then run node — no compilation overhead per request

How TypeScript Runs in the Browser

Browsers are more constrained than Node.js. They can’t access your file system, and they can’t run tsc on the fly. Every .js file the browser executes must be pre-compiled and served over HTTP.

This is why TypeScript always has a build step for frontend projects:

Your TypeScript files
    ↓ [Build tool: Vite, webpack, Parcel, etc.]
Compiler runs (tsc / esbuild / swc)

Bundler combines all .js files

dist/bundle.js
    ↓ [Uploaded to your server]

User visits your site

Browser downloads dist/bundle.js

JavaScript engine executes it

Your app works!

Modern frameworks handle this entirely automatically:

  • Vite (React, Vue, Svelte): runs esbuild behind the scenes, with tsc for type checks
  • Next.js: uses swc for blazing-fast compilation, zero config needed
  • Create React App: uses babel with TypeScript plugin

When you run npm run dev, your build tool watches every .ts file. On each save, it recompiles only the changed file (not the whole project) and triggers a hot reload in your browser. The TypeScript compilation is effectively invisible in development.


Choosing the Right TypeScript Compiler

There are four major tools for compiling TypeScript. They make different trade-offs between speed and accuracy. Let’s race them:

Compiler Speed Race

Compiling a 500-file TypeScript project — relative speed comparison

tsc Official · Type Checks
babel Plugin ecosystem · No type check
swc Rust-based · No type check
esbuild Go-based · No type check

Here’s the key insight from that race: speed and type-checking are mutually exclusive in current TypeScript tooling. The fast compilers (esbuild, swc) achieve their speed by skipping type analysis entirely — they treat TypeScript as “JavaScript with syntax to strip.” They trust that you already ran tsc to catch type errors.

CompilerSpeedType ChecksBest Use Case
tscSlow (~3s)✅ FullCI type validation, --noEmit check
esbuild⚡ ~80ms❌ NoneProduction builds, tsx runtime
swc⚡ ~100ms❌ NoneNext.js, large project transforms
BabelMedium (~1.5s)❌ NoneComplex transforms, polyfills

The production-grade pattern most teams use:

# Check types (no output)
npx tsc --noEmit

# Build fast (no type check)
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js

You get the safety of full type checking and the speed of esbuild’s output — just not at the same time.


Development vs Production: A Practical Guide

Understanding the compilation target helps you configure your tooling correctly.

In Development you want:

  • Fast rebuilds — only recompile changed files
  • Source maps — errors point to your .ts source, not the compiled .js
  • Readable output — easier to debug
# Node.js dev
npx tsx src/index.ts

# Frontend dev (Vite example)
npm run dev   # Vite watches + hot-reloads automatically

In Production you want:

  • Pre-compiled output — no compilation at runtime
  • Minified code — smaller bundle = faster load
  • Tree shaking — dead code removed
  • Type-checked — confidence before deploy
# Type check first
npx tsc --noEmit

# Then build
npm run build  # framework handles everything
You're setting up a Next.js production deployment. Which compiler does Next.js use by default to compile TypeScript?

Frequently Asked Questions About TypeScript Compilation

Can browsers run TypeScript directly?

No. All major browsers (Chrome, Firefox, Safari, Edge) only understand JavaScript. TypeScript must be compiled to JavaScript before it can run in a browser. This compilation always happens on your machine (or your CI/CD server) as a build step — never in the user’s browser at runtime.

What is type erasure in TypeScript?

Type erasure is the process of removing all TypeScript-specific syntax during compilation. Interfaces, type annotations, generics, and enums (in certain configurations) are entirely absent from the compiled JavaScript output. They only exist at compile time to help the compiler validate your code. At runtime, there is no trace of TypeScript.

What is the difference between tsc and esbuild for TypeScript?

tsc is the official TypeScript compiler. It performs full type checking, builds a complete model of your codebase, and catches type errors. It’s slower (~1–3 seconds for large projects) but authoritative. esbuild is a bundler and transpiler written in Go that strips TypeScript syntax without checking types — it’s ~40× faster but produces no type-safety guarantees on its own. Most production setups use both: tsc --noEmit to check types, and esbuild to produce the output bundle.

Does TypeScript run slower than JavaScript?

No. TypeScript compiles to plain JavaScript, and that JavaScript runs at exactly the same speed as JavaScript you’d write by hand. TypeScript types add zero runtime overhead — they are completely removed during compilation. Any performance difference between TypeScript projects and JavaScript projects comes from the code logic itself, not from TypeScript.

Is ts-node the same as tsx?

They serve the same purpose — running TypeScript directly in Node.js — but use different compilers under the hood. ts-node uses tsc and performs type checking, making it slower to start (~500–2000ms). tsx uses esbuild, skips type checking, and starts near-instantly (~50ms). For most development workflows, tsx is the better choice. For scenarios where you need runtime type validation, ts-node may be preferred.

What is a tsconfig.json file?

tsconfig.json is the configuration file for the TypeScript compiler. It lives at the root of your project and controls settings like: which JavaScript version to target ("target": "ES2022"), which files to include, whether to emit output files or just check types ("noEmit": true), and strictness settings ("strict": true). When you run tsc, it automatically reads this file.


What You’ve Learned

You now understand the complete journey TypeScript takes to become executable code:

  1. TypeScript can’t run directly — engines only understand JavaScript
  2. Compilation is type erasure — the compiler strips all type syntax, keeping only logic
  3. 4 steps — Lexing → Parsing → Type Checking → Code Generation
  4. Node.js has two pathstsx for in-memory dev, tsc + node for production
  5. Browsers require a build step — always pre-compiled, never at runtime
  6. Different compilers, different trade-offstsc for accuracy, esbuild/swc for speed

The next time you run npm run dev or npm run build, you’ll know exactly what’s happening in the background — and why it’s structured the way it is.

Next up in this series: Setting up a TypeScript project from scratch with tsconfig.json, path aliases, and strict mode.

Aditya Rawas

Written by

Aditya Rawas

Frontend developer writing deep-dives on JavaScript, TypeScript, AWS, and DevOps. Passionate about making complex engineering concepts accessible to developers at every level.

Series

Learning Typescript

  1. 01 The Benefits of TypeScript
  2. 02 How TypeScript Compiles to JavaScript