TypeScript 7 Migration Guide: tsgo, Breaking Changes, Build Times
Migrate to TypeScript 7 (tsgo): install the beta, fix the 4 breaking changes, update tsconfig, and decide if upgrading now is worth it.
On this page
TypeScript 7.0 Beta is out. The compiler is rewritten in Go, and the numbers are real: VS Code's 1.5M-line codebase goes from 78 seconds to 7.5 seconds. Sentry's monorepo drops from 133 seconds to 16.
We covered the original announcement in our post on TypeScript 7 (Project Corsa) and what it means for Next.js projects. This guide is the sequel for developers already running TypeScript 5.x or 6.x: what do you actually need to do to move your existing project to TypeScript 7, what breaks, and is it worth doing today?
What Actually Changed Under the Hood
The headline change is simple: the compiler was ported from TypeScript (running on Node.js) to Go. That's it. It is not a new type system, not a new syntax, not a new set of compiler options.
Type-checking semantics, error messages, and behavior on both valid and invalid code are structurally identical to TypeScript 6. If your code compiles cleanly under TS6, it will compile the same way under TS7 (with a small set of exceptions covered below).
What changed is execution, not behavior:
- Go compiles to native machine code: no V8 JIT warmup on every invocation, which matters most for short-lived CLI runs and editor startup.
- No garbage collector pauses interrupting type-checking on large codebases.
- True parallelism via goroutines: independent files and packages get checked across multiple CPU cores at once, something Node's single-threaded model could never do.
The new binary is tsgo. The legacy tsc binary still works through an interop shim, so existing scripts won't break overnight. But tsc is now a compatibility layer, not the primary implementation: future performance work goes into tsgo, not tsc.
Real Performance Numbers
These are measured numbers from real codebases, not synthetic micro-benchmarks:
| Project | TS 6 (tsc) | TS 7 (tsgo) | Speedup |
|---|---|---|---|
| VS Code (1.5M lines) | 78s | 7.5s | 10.4x |
| Sentry (large monorepo) | 133s | 16s | 8.3x |
| Medium Node.js backend (~100k lines) | 12s | 2.1s | 5.7x |
| Small app (~20k lines) | 3.2s | 1.1s | 2.9x |
Notice the pattern: the speedup gets bigger as the project gets bigger. That's expected, parallelism gains are most pronounced at scale, where there are more independent files and packages to spread across cores.
Smaller projects still benefit, just less dramatically. A 20k-line app going from 3.2s to 1.1s is a real improvement to editor responsiveness and incremental builds, even if it doesn't make headlines.
How to Install and Try tsgo Today
The stable typescript@7.0.0 npm package isn't out yet, it's expected late June or early July 2026. But the native preview package is functionally equivalent for most projects, and you can run it side by side with your existing setup right now.
- 1
Install the native preview package
Add
@typescript/native-previewas a dev dependency. It ships thetsgobinary alongside your existing TypeScript install, so nothing in your current setup changes yet.bash# npm npm install --save-dev @typescript/native-preview@beta # pnpm pnpm add -D @typescript/native-preview@beta # yarn yarn add -D @typescript/native-preview@beta - 2
Run tsgo and compare against tsc
Confirm the binary is installed, then run a full type check against your existing
tsconfig.json:bashnpx tsgo --version # Run a first type check against your existing config npx tsgo --project tsconfig.jsonNow run
tscthe same way and diff the output. For most projects, the error list (or lack of one) will be identical. If you see differences, jump to the breaking changes section below before assuming something is wrong.bash# Compare side by side npx tsc --noEmit npx tsgo --noEmit - 3
Install the VS Code Native Preview extension
If the command line output matches, switch your editor over too. This gets you faster IntelliSense, faster project-wide error checking, and faster startup on large projects.
- Install the TypeScript Native Preview extension from the VS Code marketplace.
- Open the command palette (
Cmd+Shift+PorCtrl+Shift+P). - Run TypeScript: Select TypeScript Version.
- Choose Use TypeScript Native Preview Version.
VS Code will now use
tsgofor in-editor type checking. If something looks off, you can switch back to the workspace version of TypeScript from the same menu at any time.
What Actually Breaks When You Upgrade
TypeScript 7 has 99%+ feature parity with TypeScript 6. But a handful of legacy patterns that were already deprecated are now fully gone. Here's the complete list.
Import assertions are replaced by import attributes
The assert keyword for import attributes has been deprecated since TypeScript 5.3 and is now mandatory to replace. Use with instead of assert.
// TS 6: deprecated, still worked with a warning
import data from "./config.json" assert { type: "json" };
// TS 7: required syntax
import data from "./config.json" with { type: "json" };JSDoc @enum is removed for .js files
This only affects .js files that use JSDoc-based types, not .ts or .tsx files. If you were relying on @enum to get enum-like behavior in plain JavaScript, replace it with a @typedef over a typeof key-union.
// TS 6: JSDoc @enum (now removed)
/** @enum {number} */
const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
};
// TS 7: typedef over a typeof key-union
const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
};
/** @typedef {Direction[keyof typeof Direction]} DirectionValue */Prototype reassignment loses special type inference
Reassigning MyClass.prototype to an object literal used to get special-cased type inference in the old compiler. tsgo doesn't replicate that special case. This pattern is rare in modern codebases, but if you have it, refactor to a proper class body or object literal.
// TS 6: special-cased prototype reassignment
function MyClass() {}
MyClass.prototype = {
greet() {
return "hello";
},
};
// TS 7: use a proper class instead
class MyClass {
greet() {
return "hello";
}
}Legacy this aliasing patterns may need refactoring
Some older code captures this into a variable (const self = this;) to work around callback scoping issues from before arrow functions were common. tsgo may type these patterns slightly differently than tsc did.
This is less common than the other three changes, since arrow functions have made this aliasing largely unnecessary for years. If tsgo --noEmit flags one of these, the fix is almost always replacing the callback with an arrow function.
tsconfig Compatibility: Removed Options
A few deprecated compiler options are removed entirely in TypeScript 7, not just deprecated.
- `importsNotUsedAsValues` and `preserveValueImports`, both replaced by
verbatimModuleSyntax. - `noImplicitUseStrict`, just remove it. Modules are always strict in TypeScript 7.
To find every removed option in your config before you upgrade, run tsgo against your project and grep for the removal warning:
npx tsgo --listFilesOnly 2>&1 | grep "Option .* has been removed"If you'd rather start clean, the TSConfigBuilder tool generates a tsconfig.json that's compatible with TypeScript 7 defaults from the ground up, useful for new projects or as a reference when auditing an existing config.
Monorepos and Project References
Monorepos are where tsgo's parallel type-checking shows up most. Independent packages get checked simultaneously across CPU cores instead of one after another, sometimes more than 10x faster on the parallel portion of the build.
Project references (the references: [] array in tsconfig.json) and --incremental work the same way they did in TypeScript 6. The relative gain from --incremental is smaller under TS7 though, since the non-incremental baseline is already so much faster that there's less room left to save.
CI/CD: What to Update
The mechanical change is swapping tsc --noEmit for tsgo --noEmit in your type-check step. You don't have to do this as a single cutover.
- name: Type check (tsc, baseline)
run: npx tsc --noEmit
- name: Type check (tsgo, preview)
run: npx tsgo --noEmit
continue-on-error: trueRunning both side by side lets you treat tsgo errors as warnings during migration. Once the two steps consistently agree, drop the tsc step and remove continue-on-error from the tsgo step.
If you're setting up GitHub Actions for type checking for the first time, our GitHub Actions tutorial covers general workflow setup, triggers, and caching.
Pre-Migration Readiness Checklist
Before you touch a single line of CI config, spend twenty minutes auditing what you are actually migrating. Most stalled migrations stall because nobody checked the dependency surface first, not because the compiler misbehaved.
Work through this list and write the answers down. Anything you cannot answer confidently is a task, not a blocker.
- Your project compiles clean on your current TypeScript version, with zero errors and zero warnings. Migrating on top of an already-red build makes every new error ambiguous.
- You know your baseline: the wall clock time of a cold
tsc --noEmiton both a developer laptop and a CI runner. - You have grepped your
tsconfig.json, and every config it extends, for the removed options listed above. - You have listed every dev dependency that imports the
typescriptpackage as a library rather than just shelling out to thetscbinary. - You know whether your test runner type-checks or only transpiles. Transpile-only runners (SWC, esbuild, Babel) are unaffected by the compiler swap.
- Your lockfile is committed and CI installs with
npm ci(or the pnpm or yarn equivalent) rather than a loosenpm install. - The migration has a named owner. A migration with no owner stalls at the first ambiguous error.
- You have agreed in advance what "done" means. A good definition: tsgo is the only type-check step in CI, and it has been green for two weeks.
A Staged Rollout Plan for a Real Codebase
A migration you can stop at any point is worth more than a fast one you cannot. Each stage below is independently useful, independently revertible, and leaves the repository in a working state.
On a small app you might run all five stages in an afternoon. On a large monorepo, budget roughly a week per stage and let each one soak in CI before moving on.
- 1
Stage 1: Shadow mode on one developer machine
One person installs the native preview locally and runs
tsgo --noEmitby hand against the existing config. Nothing is committed except, at most, the dev dependency. You are after a single yes or no answer: does the error list matchtsc?If the lists match, you are in the easy case and everything that follows is plumbing. If they differ, sort both outputs, diff them, and triage each difference against the breaking changes above before going any further.
- 2
Stage 2: Non-blocking shadow step in CI
Add
tsgo --noEmitto CI as a second, non-blocking step alongside the existing check.tscstill decides whether the pipeline passes.tsgoonly reports, and it reports its own timing so you collect data while you wait.yaml — .github/workflows/ci.yml- name: Type check (tsc, blocking) run: npx tsc --noEmit - name: Type check (tsgo, shadow) continue-on-error: true run: | start=$(date +%s) npx tsgo --noEmit || echo "tsgo reported errors" echo "tsgo finished in $(( $(date +%s) - start ))s"Let this run on every pull request for a week or two. You are collecting two things: evidence that the two compilers agree across a variety of real branches, and a timing sample large enough to be worth quoting to your team.
- 3
Stage 3: Flip which check blocks the pipeline
Once the shadow step has agreed with
tscacross enough pull requests that you have stopped checking it, swap the roles.tsgobecomes the blocking check andtscbecomes the silent one.This is the reversible moment that matters most. If something surprising shows up, you move two
continue-on-errorlines back and you are exactly where you started, with no source changes to unwind. - 4
Stage 4: Drop the tsc step
After a couple of weeks with
tsgoblocking andtscsilent, delete thetscstep entirely. Your pipeline gets shorter and every developer on the team gets faster feedback on every push. - 5
Stage 5: Move local development over
Last, update the
typecheckscript inpackage.jsonso everyone gets the fast check locally, and point your pre-commit hook at it too. A type check that finishes in a second is a check people actually leave switched on.json — package.json{ "scripts": { "typecheck": "tsgo --noEmit", "typecheck:legacy": "tsc --noEmit" } }The type-check hook is the one developers most often disable out of frustration, so it is the one that benefits most from the speedup. Our guide to Husky, Prettier, and lint-staged in Next.js covers the hook wiring itself.
How to Measure the Speed Difference on Your Own Repo
Published benchmark numbers tell you the shape of the improvement, not your number. Project structure, dependency graph depth, and available core count all move the result, sometimes by a factor of three.
Measuring it properly takes about ten minutes and gives you something concrete to put in the migration proposal instead of someone else's screenshot.
Establish a clean baseline first
Run every measurement from the same machine, on the same branch, against the same tsconfig.json. Clear incremental build state first, because a warm .tsbuildinfo file makes the second run of either compiler look artificially fast.
# Clear incremental state so every run starts cold
rm -f tsconfig.tsbuildinfo
rm -rf node_modules/.cache
# Baseline: three runs, keep the fastest
for i in 1 2 3; do
/usr/bin/time -p npx tsc --noEmit
doneKeep the fastest of the three runs, not the average. The fastest run is the one least polluted by background processes on your machine, and it is the number most likely to reproduce on someone else's.
Compare like for like, and record the context
Run the identical loop with tsgo, then record both numbers alongside the details that explain them. A bare speedup figure is unusable six months later when someone asks why you migrated.
rm -f tsconfig.tsbuildinfo
rm -rf node_modules/.cache
for i in 1 2 3; do
/usr/bin/time -p npx tsgo --noEmit
done| Metric | What to record |
|---|---|
| Cold full check | Fastest of three runs with incremental state cleared |
| Warm incremental check | Fastest of three runs with `.tsbuildinfo` left in place |
| CI wall clock | Median across at least ten pipeline runs, never a single run |
| Machine profile | CPU core count, RAM, and whether it is a laptop or a CI runner |
| Project size | File count and total lines of TypeScript under your include globs |
The CI number is the one that surprises people. CI runners often have more cores but slower single-thread performance than a developer laptop, so a parallel compiler tends to look better in CI than it does on your desk.
Watch the ratio between the cold and warm numbers too. If your warm incremental check was already fast, the migration mostly buys you cheaper cold checks, which is a CI win rather than a local-development win. That distinction changes who on the team notices the difference.
What to Do When a Dependency Has Not Caught Up
This is the most common reason a migration stalls, and it is almost never the compiler's fault. The distinction that decides how hard a given tool is to migrate is how that tool consumes TypeScript.
Tools that shell out to a binary (npm scripts, CI steps, Makefiles) are trivial to point somewhere else. Tools that import the typescript package and call into it programmatically are the ones that break, because they depend on an API surface rather than a command line.
| How the tool uses TypeScript | Typical examples | Migration difficulty |
|---|---|---|
| Shells out to the `tsc` binary | npm scripts, CI steps, Makefiles, task runners | Trivial: change the command |
| Transpiles without type-checking | SWC, esbuild, Babel with the TypeScript preset | None: they never load the compiler |
| Imports the compiler as a library | Linters, AST tooling, type-aware test transformers | Blocked until the programmatic API lands |
| Bundles its own copy of TypeScript | Some editor plugins and build frameworks | Depends on that vendor's release cycle |
For the blocked category, the answer is neither to wait idly nor to force the install. Keep two compilers installed and give each one a job it can actually do: the existing typescript package stays for anything that imports it as a library, and the native preview handles command line type-checking only.
Forcing an install past a peer dependency conflict is the failure mode to avoid. It does not fix the incompatibility, it just moves the error from install time to run time, where the stack trace points into someone else's internals instead of your config. If you are already staring at an ERESOLVE wall of text, the ERESOLVE Explainer will name the two packages that actually disagree.
Deciding whether to wait or work around
For each blocked dependency, work out which of three buckets it falls into before you spend any engineering time on it.
- Actively maintained with a tracked issue. Subscribe to the issue, keep the side-by-side setup, and move on. Waiting is cheaper than patching.
- Maintained but no visible movement. Open an issue with a minimal reproduction. A clear repro is often the difference between a fix landing in weeks and one landing in quarters.
- Unmaintained. This is a replacement decision, not a migration decision. Do not let a dead dependency hold the compiler upgrade hostage: scope the replacement as separate work and continue.
One thing worth checking before you assume a tool is blocked: does anything in your pipeline actually depend on it for correctness, or is it there out of habit? Teams regularly find a type-aware plugin in their build that nothing reads the output of. Deleting it is faster than migrating it.
CI Considerations and Caching
A faster compiler makes the rest of your pipeline's overhead visible. When type checking drops from ninety seconds to eight, a forty second dependency install stops being background noise and becomes the next thing worth fixing.
Cache dependencies, not compiler output
The native preview ships a platform-specific native binary, which changes what is safe to cache. Caching your package manager's download store, keyed on the lockfile, is safe and effective. Caching an entire installed node_modules directory across jobs that run on different operating systems or CPU architectures is not, because you can restore a binary built for the wrong platform.
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsgo --noEmitnpm ci plus a package manager cache is the right default. It respects the lockfile exactly, and the cache key changes automatically when the lockfile changes, so you never restore a stale binary next to a new lockfile.
If your project uses incremental builds, the .tsbuildinfo file is worth caching separately, keyed on a hash of your source tree. Just be aware that the payoff is smaller than it used to be: the non-incremental baseline is now fast enough that restoring and validating a cache can cost more than it saves on smaller projects. Measure before you add it.
Fail fast, and fail cheap
Put the type check early in the pipeline, ahead of the bundle build and the test suite. It is now the cheapest meaningful signal you have, and an error that surfaces in eight seconds costs a developer almost nothing compared to one that surfaces after a four minute build.
Resist the urge to split the type check into its own parallel job purely to make the pipeline graph look tidy. A separate job pays container startup and dependency install costs all over again, and on most pipelines that overhead is now larger than the type check itself.
Rollback Strategy
The reason this migration is low risk is that there is very little state to unwind. You are not rewriting source code, not changing your type system, and not changing what your build emits. You are swapping which binary reads the same tsconfig.json.
That means rollback is a config change, and it should be one you can land in a single small pull request at any stage of the rollout.
| If this goes wrong | Roll back by |
|---|---|
| tsgo reports errors that tsc does not | Make the tsgo step non-blocking again, then triage the diff offline without holding up merges |
| A tool breaks after you bump the typescript package | Revert the version bump in `package.json` and the lockfile. Leave the native preview installed. |
| CI got slower, not faster | Check whether install time now dominates. Roll back the caching change rather than the compiler. |
| A removed tsconfig option turned out to be load-bearing | Restore the option, pin the previous compiler, and configure the replacement before trying again |
| Something is wrong and you cannot tell what | Revert to the last commit where tsc was the blocking check. By design that is never more than one stage back. |
Two habits make all of this cheap. First, keep each stage of the rollout in its own commit, so reverting one stage never drags another along with it. Second, never combine the compiler swap with an unrelated tsconfig.json cleanup in the same pull request. If you change two things and the build goes red, you have doubled your debugging surface for no benefit.
Team Communication and Rollout Sequencing
On a codebase with one or two contributors, the technical plan is the whole plan. On a codebase with thirty, sequencing matters more than the commands do.
The failure mode is not a broken build. It is five people independently discovering the same new error on their branches on the same afternoon, and each spending an hour on it separately.
| Team shape | Rollout approach |
|---|---|
| 1 to 3 developers | Run all five stages back to back. Post a one-line note when the blocking check changes. |
| 4 to 15 developers | One owner runs stages 1 to 3. Announce before stage 3, since that is the first point a new error can block someone's merge. |
| 15 or more in a single repo | Write the migration note into the repo itself. Leave stage 2 running for a full sprint before flipping. |
| Large monorepo, many owning teams | Migrate package by package. Start with a package that has few dependents and an engaged owner. |
What to actually tell the team
Keep the announcement short and answer the three questions people will genuinely have.
- What changes for me? Usually nothing, right up until stage 5 changes the local
typecheckscript. - What do I do if I see a new error? Name the person to ping and link the breaking changes section, so nobody debugs it from scratch.
- Can I opt out today? During stages 1 through 3, yes. Saying so up front removes most of the resistance you would otherwise get.
Time the flip in stage 3 for the start of a week, not a Friday afternoon and not the day before a release freeze. The change is low risk, but low risk is not zero risk, and you want the migration owner online when it lands.
On a large monorepo, sequence by blast radius rather than by size. A leaf package with no dependents is a better first candidate than the shared types package everything imports, even though the shared package would show the bigger speedup. Get the process right somewhere low stakes, then apply it where it pays.
Finally, write down the numbers you measured and the date you flipped each stage. When the stable release lands and someone asks whether it is worth upgrading the rest of the organisation, that short record is the entire business case, already written.
Should You Upgrade Now?
| Situation | Recommendation |
|---|---|
| New project, no legacy patterns | Use tsgo from day one |
| Existing project, compile time > 30s | Try the beta today |
| Existing project, compile time < 5s | Wait for stable (late June 2026) |
| Project uses `importsNotUsedAsValues` or JSDoc `@enum` | Fix deprecated patterns first, then upgrade |
| Large monorepo on a tight timeline | Run both in CI, plan migration for Q3 2026 |
| Legacy patterns present (prototype reassignment, `assert` imports) | Audit first with `tsgo --noEmit` to see error count |
Microsoft describes the Beta as "highly stable, highly compatible, and ready to be put to the test in your daily workflows and CI pipelines today."
Greenfield projects and teams comfortable running a beta compiler have no real reason to wait. Everyone else can run tsgo --noEmit alongside their existing setup this week and know within minutes whether the migration is trivial or needs cleanup first.
Frequently Asked Questions
When will TypeScript 7 be stable?
Late June or early July 2026 is the current target for the stable typescript@7.0.0 release. The Beta is already described by Microsoft as production-ready for daily workflows and CI pipelines, so the stable release is mostly about removing the beta label rather than major behavioral changes.
Is TypeScript 7 backward compatible with TypeScript 6?
Yes, with 99%+ feature parity and identical type-checking semantics for the vast majority of code. The small list of breaking changes (import attributes, JSDoc @enum removal for .js files, prototype reassignment, and this aliasing edge cases) is covered in full above.
What is tsgo?
tsgo is the new compiler binary written in Go, shipped as part of TypeScript 7 and currently available through the @typescript/native-preview package. It replaces the Node.js-based compiler for type-checking and emit.
The tsc binary still works through an interop shim for backward compatibility, but it won't receive future performance work. New optimizations land in tsgo.
How do I install the TypeScript 7 beta?
Install the native preview package as a dev dependency, then run a type check with tsgo:
npm install --save-dev @typescript/native-preview@beta
npx tsgo --noEmitDoes TypeScript 7 support all TypeScript 6 features?
Yes, except for the small set of removed legacy patterns: import assertions (use import attributes with with instead of assert), JSDoc @enum in .js files, and special-cased prototype reassignment inference.
All four are covered above with migration paths. For most projects on modern syntax, none of these apply and the upgrade is just a version bump.
How do I benchmark tsgo against tsc accurately on my own repo?
Clear incremental state first, run each compiler three times, and keep the fastest run rather than the average. A warm .tsbuildinfo file or a populated cache directory will make whichever compiler you ran second look better than it is.
rm -f tsconfig.tsbuildinfo && rm -rf node_modules/.cache
for i in 1 2 3; do /usr/bin/time -p npx tsc --noEmit; done
rm -f tsconfig.tsbuildinfo && rm -rf node_modules/.cache
for i in 1 2 3; do /usr/bin/time -p npx tsgo --noEmit; doneRecord your CPU core count alongside the timings, because the parallel speedup scales with cores. Then repeat the comparison in CI, where the core count and single-thread speed are usually both different from your laptop.
What if one of my build tools does not support TypeScript 7 yet?
Keep both compilers installed rather than forcing an install past the conflict. Anything that imports the typescript package as a library keeps resolving against your existing version, while the native preview handles command line type-checking only. The two coexist happily because they are separate packages.
The tools most likely to hit this are linters, AST manipulation libraries, and type-aware test transformers. Our post on what broke in ESLint, ts-jest, and ts-morph walks through the exact side-by-side setup and the errors each tool produces when you get it wrong.
How long does a TypeScript 7 migration actually take?
The hands-on work is usually measured in hours, not weeks. Installing the preview, diffing the two error lists, and wiring a shadow CI step is an afternoon on most codebases.
The calendar time is longer, and deliberately so. The staged rollout above spends most of its duration waiting: letting the shadow step run across enough real pull requests to build confidence before anything blocks a merge. Budget an afternoon of work spread across three to six weeks of soaking, and considerably longer for a monorepo you are migrating package by package.
Related Articles
TypeScript 7 (Project Corsa): What Next.js Devs Need to Know
TypeScript 7 rewrites the compiler in Go for 10x faster builds. Here's what it means for your Next.js project and what to do right now.
GitHub Actions Tutorial: CI/CD from Push to Deploy (2026)
Learn GitHub Actions: write your first workflow, run tests automatically, use secrets safely, deploy via SSH, cache dependencies, and run matrix builds.