TypeScript vs JavaScript Compilers: tsc, swc, and esbuild
What TypeScript adds to JavaScript
TypeScript is not a new runtime. It is JavaScript with an optional static type system layered on top. TypeScript source code is not executed directly; it is compiled, or more precisely transpiled, to plain JavaScript, which then runs in V8, SpiderMonkey, JavaScriptCore, or any other JavaScript engine. The TypeScript compiler removes type annotations and outputs JavaScript that behaves identically at runtime.
This means TypeScript has two implementation layers: the transpiler (what turns TypeScript into JavaScript) and the JavaScript engine (what executes the resulting JavaScript). The transpiled_to relationship in the Language Lineage dataset records the first layer; compiler_written_in and runtime_written_in capture the second.
tsc: the official TypeScript compiler
tsc is the TypeScript compiler developed and maintained by Microsoft. Its most important property is that it is self-hosting: tsc is written in TypeScript. Each new version of tsc is compiled by the previous version. This mirrors the bootstrap pattern of mature self-hosting compilers like GHC (Haskell) and rustc (Rust).
tsc performs two jobs: type checking and transpilation. Type checking analyzes the type annotations and infers types, catching errors at compile time. Transpilation strips type annotations and transforms TypeScript-only syntax (decorators, enums, parameter properties) to plain JavaScript. Both jobs happen in a single pass, but they can be decoupled: tsc --noEmit only type-checks, while tools like esbuild only transpile.
tsc is not optimized for speed. It builds a full program graph for type checking, which is computationally expensive for large codebases. A cold tsc build on a large TypeScript project (say, 200,000 lines) can take 30 to 60 seconds. The project mode (--build) and incremental compilation (--incremental) reduce this significantly for rebuilds.
swc: a TypeScript transpiler written in Rust
swc (Speedy Web Compiler) is a TypeScript and JavaScript transpiler written in Rust. It is developed by Donny/강동윤 and sponsored by Vercel. swc is used under the hood in Next.js, Deno (for TypeScript stripping), and several other build tools.
swc does not perform TypeScript type checking. It treats type annotations as syntax to strip, parsing them with a handwritten Rust parser and emitting JavaScript without any type analysis. This limitation is intentional: skipping the type checker is what makes swc 20 to 70 times faster than tsc for transpilation. swc is the right choice when a separate type-checking step runs in CI (via tsc --noEmit) and raw build speed matters for the development loop.
The rewritten_in relationship in the dataset reflects tools that were significantly reimplemented; swc is a distinct project, so the dataset records it as a separate tool with its own compiler_written_in edge to Rust.
esbuild: a bundler and transpiler written in Go
esbuild is a JavaScript and TypeScript bundler and transpiler written in Go. It was created by Evan Wallace (co-founder of Figma) and first released in 2020. esbuild is used by Vite as its development-server transpiler and by many other modern build tools.
Like swc, esbuild does not type-check TypeScript. It strips types and emits JavaScript, relying on the user to run tsc --noEmit separately. esbuild's speed advantage comes from Go's execution model and careful design: a single-pass parser, minimal allocations, and deliberate avoidance of complex AST transformations. An esbuild build that would take 90 seconds in webpack typically takes under a second.
esbuild also bundles: it resolves imports, concatenates modules, and applies tree-shaking. This makes it a bundler plus transpiler in one tool, whereas swc focuses purely on single-file transpilation.
Comparison
| Tool | Written in | Type-checks? | Speed | Primary use |
|---|---|---|---|---|
| tsc | TypeScript (self-hosting) | Yes | Slow (full program analysis) | Official compiler, type checking, IDE integration |
| swc | Rust | No (strip only) | 20-70x faster than tsc | Fast transpilation in build pipelines (Next.js, Deno) |
| esbuild | Go | No (strip only) | 10-100x faster than webpack | Fast bundling and transpilation (Vite dev server) |
Type-only transpilation vs full compilation
One nuance: TypeScript's type system is erased at runtime. There are no type annotations at all in the JavaScript output. This means TypeScript types have no runtime cost and cannot be inspected at runtime via reflection (unlike Java generics, which are mostly erased, but with some residual type information). Runtime type checking (narrowing, instanceof, discriminated unions) in TypeScript is built from ordinary JavaScript constructs, not from preserved type metadata.
This also means TypeScript does not generate any code for its type annotations; the JavaScript output from tsc --noEmit is structurally identical (ignoring whitespace) to what you would get from a fast stripper like swc or esbuild. The type checker is a compile-time analysis tool, not a runtime component.
The full implementation chain
TypeScript code runs through two hops before reaching the CPU. First, a transpiler (tsc, swc, or esbuild) converts TypeScript to JavaScript. Second, a JavaScript engine (V8, SpiderMonkey, or JavaScriptCore, all written in C++) executes the JavaScript. So the full chain is: TypeScript source to tsc/swc/esbuild (TypeScript/Rust/Go) to JavaScript to V8 (C++) to machine code. This chain is what "what is TypeScript written in?" is really asking about.
TypeScript is unusual among major languages in that it has no dedicated runtime at all. There is no "TypeScript virtual machine." The language is purely a compile-time layer, and the runtime is whatever JavaScript engine the output runs in. This is fundamentally different from Java or Python, which have dedicated runtimes (the JVM and CPython) that are written in specific languages and have specific performance characteristics.
See the TypeScript language page and the transpiled_to relationships for the full dataset view.
Explore TypeScript Relationships in Graph →