The number everyone quotes is 10x. Microsoft says 8x to 12x on full builds, and the VS Code repo went from close to a minute of project load to about ten seconds. I didn't believe any of it until I ran it on my own code, because compiler speedups tend to get measured on the repos the compiler team optimises for.
Here's what I actually got, on a six package pnpm monorepo with Turborepo: about 41,000 lines of TypeScript, a Next.js app, a UI package and four libraries. tsc --noEmit across all eight typecheck tasks took 38 seconds cold on TypeScript 5.9. On 7.0.2 it takes 4.1 seconds. In the editor, where it matters more, the first hover after opening one of the big files used to lag for a second or two, and now it doesn't lag at all.
So the number is real. The migration isn't free, though, and the parts that cost time weren't the ones I expected.
What the port is, and what it is not
TypeScript 7 is a port of the compiler and language service from TypeScript to Go. The team was careful to call it a port and not a rewrite, and the word matters: type checking is meant to behave exactly as in 6.x, the last TypeScript-in-TypeScript release and the bridge version. If 7 reports an error in your code that 6 didn't, that's a bug, not a new rule.
What you get is a native binary, real parallelism across files, and no garbage collected JavaScript heap between you and the checker, which is where the speed comes from. There's no new inference algorithm, no new type system feature, and nothing new to learn about the language.
What you don't get in 7.0 is a stable programmatic API. Anything that calls ts.createProgram, walks the AST with ts.forEachChild or builds on the language service directly won't work against the Go compiler yet. Microsoft says 7.1 brings a new API, and until then those tools stay on the 6.x line.
That paragraph is the entire migration risk. Everything else is a version bump.
Step one: find out who talks to the compiler API
Before you touch a package.json, list every tool in your build that uses TypeScript as a library instead of as a command. In my monorepo I started with:
pnpm why typescript
The output was longer than I'd hoped: Next.js, Biome, tsup, a custom MDX plugin, a codegen script for the site config schema, and typescript-eslint, pulled in transitively by something I'd forgotten about. Not all of those matter. For each one, the question is whether it runs the compiler or just needs the typescript package to exist.
Biome doesn't touch the TypeScript API at all, since it has its own parser. Next.js 16 with Turbopack does its own type stripping and only calls tsc for the typecheck step during next build, which is a command line call and works fine. tsup uses esbuild to transpile and tsc for declaration files, also as a command. The codegen script was the problem. It imported typescript and walked a schema file to produce a JSON document, so it had to stay on 6.x.
The clean way to run both is to keep typescript at 7 in the workspace root and pin the one package that needs the API to 6:
{
"name": "@zeybek/codegen",
"devDependencies": {
"typescript": "6.0.4"
}
}
pnpm isolates that install, so the script sees 6 and everything else sees 7.1
Step two: the config cleanup you were putting off
TypeScript 6 deprecated a batch of tsconfig options and 7 removes them. These are the ones that hit me.
baseUrl is gone. If you only had it so paths would work, delete it, because paths now resolves relative to the config file. If you had it so bare imports resolved from src, you'll need to switch those imports to a path alias.2
moduleResolution: node (the old one, now called node10) is out. By 2026 almost everything should be on bundler or nodenext anyway. My UI package was still on node because nobody had a reason to change it. Moving it to bundler turned up two deep imports in the style of lodash/debounce that needed the .js extension under the new rules.
target: ES5 and ES3 are gone. I didn't have those, but I've seen legacy configs that still do.
esModuleInterop and allowSyntheticDefaultImports are now always on. Delete them from the config, they're just noise.
The deprecation messages in 6 tell you exactly which line to fix, so do it in this order: upgrade to 6.0, run tsc, fix every deprecation warning, then upgrade to 7. Jumping straight from 5.9 to 7 gets you the errors without the helpful explanations.
Step three: the actual upgrade
pnpm
pnpm -r update typescript@7 @types/node@latest
pnpm typecheck
npm
npm install -D typescript@7 --workspaces
npm run typecheck --workspaces
The typescript package on npm now ships the Go binary for your platform as an optional dependency, the way esbuild and Biome do. The tsc command has the same name and takes the same flags. You don't need the @typescript/native-preview package from the preview period any more, and if it's installed, remove it, because having both on the path is confusing.
I hit one change in behaviour straight away. TypeScript 7 checks projects in parallel and reports errors in a different order from 6. If you had a test that snapshotted tsc output, or a CI step that grepped for the first error, the order isn't deterministic across runs any more. I had a Turborepo task that compared error counts between branches. The counts still match and the order doesn't, which is fine.
The editor is where you feel it
The command line speedup is nice for CI. The editor is the reason to do this now.
VS Code 1.104 and later ship the native language service behind typescript.experimental.useTsgo, and with TypeScript 7 in the workspace it becomes the default. Day to day, this is what changes.
Hover, go to definition and find all references on a large file no longer have the half second pause. Renaming a symbol across the monorepo went from "start it and go make tea" to under a second, for the 600 reference rename I tried. The "Loading TypeScript project" spinner at startup, which used to sit there for 20 to 30 seconds on this repo, is gone.
Memory is the other thing. On this monorepo the old language service sat at 1.4 GB after an hour of editing. The Go one sits at 380 MB and stays there. On a 16 GB laptop with a browser open, that decides whether the fan spins up.
In other editors the language server side is the same binary, so Neovim with typescript-language-server picks it up as soon as the plugin points at the new tsserver path.3
What broke, honestly
Two things broke, both small and both my fault.
The first was a .d.ts file with a triple slash reference to a types package that no longer existed. TypeScript 5 silently ignored it. 7 reports it as an error, which is correct, so I deleted the line.
The second was a type test with @ts-expect-error above a line that 6 flagged as an error and 7 doesn't. That looked like a semantic difference, so I dug in. The line was a generic call with a conditional type that 5.x resolved to never because of a known inference limitation. 6 fixed the inference and 7 inherits the fix. The @ts-expect-error had been papering over a compiler bug that no longer exists, so the "unused expect error" report was the compiler telling me the truth. That's the only type checking difference I found in 41,000 lines.
CI, briefly
Before the migration, the GitHub Actions job that ran tsc across the monorepo took about 50 seconds including setup. Now it takes about 15, and 11 of those are the runner starting and pnpm restoring its store from cache.4 For a job that runs on every push to every branch, that let me move typechecking from a merge-only check to a per-push check without anyone complaining about the wait, and it now catches type errors two hours earlier on average.
When not to upgrade yet
If your build depends on ts-morph, a custom transformer plugin through ttypescript or ts-patch, a generator that walks the AST, or an older typescript-eslint that runs type aware rules through the TypeScript API, wait for 7.1, or pin those tools to 6 the way I did with the codegen script. The type aware lint rules are the common case.5
If you're on Angular, follow Angular's own guidance, because its CLI is more tightly tied to the compiler than most frameworks.
One more case. A very large single project, say a 5,000 file include with no project references, will see the smallest relative gain. The Go compiler parallelises across files and is still fast, but the old advice to split a monolith into referenced projects still holds. In this monorepo each package is already its own program, so the parallelism had eight things to chew on from the start, and I suspect that's part of why my numbers landed at the top of Microsoft's range and not the bottom. If yours land at the bottom, project references are the next thing to try, and they were worth doing before 7 as well.
For everyone else: do the 6 step first, fix the deprecations, then switch to 7. The upgrade itself is a version number. The speed is real, the biggest single improvement to my everyday tooling since Turbopack, and I've got no reason to go back.
Tip
Check
pnpm why typescript(or the npm equivalent) before anything else. The list of packages that import the compiler as a library is your whole risk. If it's empty, the upgrade takes ten minutes.
Originally published at zeybek.dev.
-
With npm or Yarn and hoisting you'll need an alias or an override, and it's fiddlier. ↩
-
I had four files importing
shared/componentswithout a leading@/, and they'd worked for years by accident. ↩ -
Zed and JetBrains shipped support during the RC period. ↩
-
The typecheck itself is the 4 seconds from the top of this post. ↩
-
typescript-eslinthas a compatibility layer in progress, but as of this week it runs against 6. ↩
Top comments (0)