TypeScript 7 Went Native: What Actually Changes And What Doesn't

dev.to

Everyone's heard by now that TypeScript "went native." And I keep seeing the same wrong conclusion drawn from it: that TypeScript now somehow runs without being compiled, that the build step is gone, that your .ts files execute directly.

None of that happened. Your browser still can't run TypeScript. Node still can't type-check it. The thing that went native isn't your code. It's the compiler.

And honestly, that's the better story.

What "Native" Actually Means

For its entire life the TypeScript compiler has been written in TypeScript. tsc, the thing you run in CI and tsserver the thing feeding your editor its red squiggles were JavaScript programs executing on Node.js. Every type-check of your million-line codebase was itself a JavaScript program churning through a pointer-heavy graph of type objects, single-threaded with JIT warmup and garbage-collector pressure along for the ride.

TypeScript 7 replaces that with a compiler written in Go, compiled ahead of time to a native binary. Microsoft announced the port in March 2025 with Anders Hejlsberg TypeScript's lead architect fronting the effort and shipped it as TypeScript 7.0 on July 8, 2026.

So when you read "native TypeScript," expand it to "natively compiled TypeScript toolchain." The pipeline looks like this:

  • Before: your .ts files go into a compiler written in JS, running on Node, and out come type errors plus emitted .js.
  • After: your .ts files go into a compiler that's a native Go binary, and out come the same type errors plus the same emitted .js.

The first box and the last box didn't change. Only the middle one did. That's the whole announcement and it's enough to be a big deal.

Where The 10x Comes From

The headline number holds up. These are Microsoft's published full-build benchmarks from the 7.0 release post:

Codebase TypeScript 6 TypeScript 7 Speedup
VS Code 125.7s 10.6s 11.9x
Sentry 139.8s 15.7s 8.9x
Playwright 12.8s 1.47s 8.7x

The original announcement benchmarks told the same story on type-checking alone: VS Code's 1.5 million lines went from 77.8s to 7.5s, TypeORM from 17.5s to 1.3s, tRPC from 5.5s to 0.6s. The multiplier is remarkably consistent across project sizes which tells you it's not some cache trick that only helps huge repos. It's the floor moving.

Here's the part most coverage skips: native compilation alone doesn't buy you 10x. Going from JIT-compiled JavaScript to ahead-of-time Go gets you a chunk of it. The rest comes from shared-memory multithreading, something the old compiler structurally couldn't do. JavaScript's worker threads can't share object graphs; they pass messages and copy data. A type-checker whose entire job is traversing one giant shared graph of types was stuck on a single core. In Go the checker splits work across parallel workers that all read the same memory.

TypeScript 7 exposes this directly. Type-checking runs on 4 workers by default, and you can turn the dial:

# default: 4 type-checking workers
npx tsc -p tsconfig.json

# crank it up on a beefy CI box
npx tsc -p tsconfig.json --checkers 8
Enter fullscreen mode Exit fullscreen mode

With --checkers 8, Microsoft's VS Code benchmark drops to 7.51s, a 16.7x speedup over TypeScript 6. That's the compounding effect: native code made the work faster, and concurrency made the work parallel.

Memory moved in the right direction too: the 7.0 post reports aggregate build memory down 18% on VS Code and 26% on Bluesky's codebase. Not headline material next to 10x but if you've ever watched tsc eat 4GB in CI, you'll take it.

What Changes In Your Day

Numbers on Microsoft's benchmark machines are nice. What matters is where the time comes back in your week. It shows up in three places.

The editor. This is the one you'll feel first, because you feel it hundreds of times a day. Project load in the original benchmarks went from 9.6 seconds to 1.2 seconds on VS Code's codebase. The 7.0 release measured opening a file with errors dropping from 17.5 seconds to under 1.3. If you've worked in a large monorepo you know the ritual: open a file, wait, watch "Initializing JS/TS language features" spin, go get coffee, come back to squiggles. That ritual is what dies here. The language service was also rebuilt on the Language Server Protocol and Microsoft reports over 80% fewer failing commands and over 60% fewer crashes than the 6.0 server. Fewer "restart TS server" moments is its own quality-of-life feature.

CI. The typecheck step has been the quiet bottleneck of a lot of pipelines: too important to skip, too slow to love. The release post has real production numbers here. Slack cut CI type-checking from 7.5 minutes to 1.25 and eliminated 40% of their merge queue time. Canva's error detection went from 58 seconds to 4.8. Microsoft's own News Services team reports around 400 hours a month no longer spent waiting on CI builds. When the typecheck stops being the long pole, merge queues drain faster and "just rerun CI" stops costing you a coffee break.

The workarounds you stop needing. This one's subtle and, I think, the most interesting. A slow compiler doesn't just cost time. It bends architecture. skipLibCheck: true is in half the tsconfigs on GitHub not because anyone wanted less checking but because checking node_modules types was too expensive. Project references with their composite builds and .tsbuildinfo choreography, exist substantially as a performance escape hatch and plenty of monorepos were split along lines chosen to keep tsc bearable rather than lines that made sense for the domain. When the full check of a 1.5M-line codebase takes 10 seconds the pressure behind all of that eases. You don't have to un-do your project references tomorrow. You just stop reaching for the gymnastics the next time and that changes how codebases grow from here.

What Doesn't Change

Now the myth-busting half of the ledger, because this list is exactly as important:

  • Your emitted JavaScript. Same input, same output. The build artifact your users download doesn't change by a byte's worth of behavior.
  • Your runtime. Nothing about how your code executes is different. This is a compile-time story, full stop.
  • The type system's rules. TypeScript 7 is built to match TypeScript 6.0's type-checking behavior. Code that compiled cleanly on 6.0 should compile identically on 7.0. Your types didn't get stricter, looser, or smarter. They got checked faster.

This wasn't luck. It's the reason the team chose Go in the first place. The existing compiler is a decade of accumulated behavior: pointer-heavy tree traversals, shared mutable state, thousands of subtle decisions encoded in its structure. The team explicitly framed the project as a port, not a rewrite, translating the existing codebase nearly function-for-function to preserve its exact behavior. Go won because idiomatic Go could mirror the existing code's shape; a Rust version would've forced them to rethink memory and mutation from scratch, turning a port into a rewrite and a compatibility promise into a prayer.

I'd call that the most underrated engineering decision in the whole project. The boring choice, the one that let them prove behavior stayed identical, is the reason you can adopt a compiler rewrite with roughly the same risk profile as a minor version bump.

This Is Not Node's Type Stripping

People keep conflating these two, and they're solving opposite problems.

Since Node 22.6.0, and enabled by default from 23.6.0 onward, Node can run TypeScript files directly by stripping the types out. And "stripping" is delightfully literal: Node replaces your type annotations with whitespace and executes what's left, so line and column numbers still match your source. No type-checking happens. None. You can declare const port: number = "definitely not a number" and Node will run it without a complaint.

That's also why only erasable syntax works, meaning anything you can delete without changing runtime behavior:

// runs fine under Node's type stripping:
interface User {
  id: number;
  name: string;
}
const greet = (user: User): string => `Hi, ${user.name}`;

// throws ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, because an enum
// is not erasable: it generates real runtime code
enum Role {
  Admin,
  Member,
}
Enter fullscreen mode Exit fullscreen mode

Enums, namespaces with runtime code, and constructor parameter properties all generate JavaScript, so deleting them would change behavior, so Node's default mode refuses them.

So the line between the two:

  • Node's type stripping answers "can I run this .ts file without a build step?" It executes your code and checks nothing.
  • The native TypeScript compiler answers "can I check this code before it ships?" It validates everything and got 10x faster at it.

They're complementary, not competing. A perfectly modern setup in 2026 is Node executing your .ts files directly in development while the native tsc runs the actual type-check in your editor and CI. One removes a build step; the other makes the safety net fast enough that you never think about it.

Timeline And Migration, Honestly

Where things stand as of mid-2026:

TypeScript 7.0 is GA. It's the typescript package on npm, and the binary is still called tsc. If you played with the preview, that was @typescript/native-preview with a tsgo binary; that era is over, and the native compiler is now just... TypeScript.

The 6.x line continues. The JavaScript-based compiler lives on as TypeScript 6, maintained in parallel until the native port fully takes over. There's even a compatibility package that installs it as tsc6 alongside 7, which matters because of the next point.

The programmatic API is the honest asterisk. TypeScript 7 doesn't yet expose a stable API for tools that consume the compiler as a library. That means typescript-eslint's type-aware rules, and the template type-checking behind Vue, Svelte, and Astro, still need TypeScript 6 under the hood. The stable API is slated for 7.1. Until then, tooling-heavy setups run both:

package.json

{"devDependencies":{"typescript":"npm:@typescript/typescript6@^6.0.2","@typescript/native":"npm:typescript@^7.0.2"}}
Enter fullscreen mode Exit fullscreen mode

Your fast builds and editor experience come from 7; your lint toolchain keeps the 6 API it needs. Clunky, temporary, and worth it.

Some defaults tightened. 7.0 flips strict on by default, defaults module to esnext, and drops long-deprecated targets like ES5 and AMD/UMD output. If your tsconfig already says what it means, and it should, you'll barely notice. If you're on a codebase that never turned strict on, the compiler didn't break your code; it just stopped pretending the old defaults were fine.

Note
If you maintain a plain tsc-built project, the upgrade is about as boring as upgrades get: bump the package, run the build, read the handful of config warnings. The teams that should wait a beat are the ones whose toolchain reaches into the compiler API. Check your lint setup and framework tooling before flipping the switch.

A Tooling Story, Not A Language Story

TypeScript 7 adds nothing to the language and that's precisely why it matters. Every previous major version gave you new type-system toys. This one gives you back the time you've been quietly paying at every keystroke, every save, every push and it retires a whole category of architectural decisions that were never really architecture just coping mechanisms for a slow compiler.

The 10x makes headlines. Watching what large TypeScript teams stop doing because of it will be the real payoff.


P.S. Thanks for taking the time to read this article! The ideas and opinions expressed here are my own. English is not my first language, so I use AI to help correct grammar and make my writing clearer and easier to read. If anything still sounds a little awkward, I appreciate your understanding!

Originally published at nazarboyko.com.

Enjoyed this one? Let's stay in touch — I'm on LinkedIn, always happy to chat, swap ideas, or just say hi. 👋

Source: dev.to

arrow_back Back to News