← Retour au Blog

The TypeScript 6.0 Migration Recipe: Upgrading Without Breaking Your App

typescript
migration
tooling

TL;DR: TypeScript 6.0 flipped several major default settings (including strict: true and module: esnext) to force codebases to clean up before TS 7.0 introduces the new Go-based compiler. Run @andrewbranch/ts5to6 to handle the grunt work, manually fix your types array and rootDir, and stop relying on deprecated ES5 and AMD targets before the TS 7.0 stable release expected within about two months.

A dual-monitor desk setup with TypeScript 6.0 displayed on screen

That’s it. It’s finally time to pay down the accumulated technical debt. Maybe you volunteered, or maybe someone “volunteered” you. Either way, you’ve hit that familiar wall: you are forced to update your project’s core dependencies because you literally can’t add any new libraries without downgrading versions to avoid conflicts.

But let’s get one thing straight right out of the gate: migrating to TypeScript 6.0 (released March 23, 2026) is not your standard weekend version bump.

This is likely the last major release of the current JavaScript-based TypeScript compiler. Microsoft altered several major default compiler settings in one swing to forcefully clean up our collective codebases before they unleash TS 7.0 Project Corsa, the ground-up rewrite in Go. And the clock is ticking: the TS 7.0 Beta just dropped on April 20, 2026, with the stable release expected within about two months.

If you ignore the 6.0 migration now, your jump to 7.0 isn’t going to be a refactor; it’ll be a rewrite. Here is my exact recipe for surviving the TS 6.0 migration without completely breaking your production builds.



1. Before You Touch Anything: Run the Migration Tool

Don't be a hero and try to manually grep through your tsconfig.json. The TS team provided a migration script. I always run this first.

npx @andrewbranch/ts5to6

This automates the tedious stuff: translating baseUrl to paths, explicitly setting your rootDir, migrating assert {} to with {}, and flattening out those cursed multi-tsconfig extends chains.

Once that runs, do the actual bump:

npm install -D typescript@latest
npx tsc --noEmit

Look at your terminal. That wall of red text? That’s what we are fixing today.

2. Breaking Changes: The 8 Defaults You Must Know

TS 6.0 flipped the script on defaults. Before we dive into the specific fixes, here is the cheat sheet of exactly what changed under the hood.

Setting Old Default New Default Impact
strict false true High
module CommonJS esnext High
types auto-discover all [] High
target ES3 Latest ES Medium
esModuleInterop false true Medium
rootDir inferred tsconfig folder Medium
noUncheckedSideEffectImports false true Low-Medium
libReplacement true false Low

Here is how to handle the changes that are going to hurt the most.

strict: true — The Wall of Errors

  • Question: Do I have the sprint budget to fix all strict errors now, or do I explicitly opt-out and write a tech-debt ticket?
  • Context: TypeScript finally ripped the band-aid off. strict defaults to true. This enables 8 core checks immediately (including strictNullChecks and noImplicitAny).
  • Fix: Either fix the typing, or explicitly set "strict": false in your config to stop the bleeding.
// TS 5.x: This was fine if strict was false.
// TS 6.0: ERROR - Parameter 'id' implicitly has an 'any' type.
function fetchUser(id) {
  return db.get(id);
}
 
// The Fix
function fetchUser(id: string) {
  return db.get(id);
}

types: [] — The Sneaky Node.js Breaker

  • Question: What global @types packages does my project actually rely on?
  • Context: TS 6.0 stops auto-discovering all @types/* packages in your node_modules. If you are suddenly seeing Cannot find name 'process' or Cannot find name 'Buffer', this is why.
  • Fix: Explicitly declare your global types in tsconfig.json: "types": ["node", "jest"].

target: Latest ES — The Moving Target

  • Question: Does my build process expect a specific JavaScript output version?
  • Context: The target default is no longer fixed to a legacy baseline. It now resolves to the most recent stable ECMAScript version immediately before esnext (currently es2025), and will keep moving forward.
  • Fix: Always set your target explicitly (e.g., "target": "es2022") for build reproducibility. Don't let the compiler guess for you.

module: esnext — ESM by Default

  • Question: Is my project browser-only (bundler handles it), Node.js CJS, or ready to go full ESM?
  • Context: If you are running a Node.js project using require() and CommonJS, TS 6.0 assumes ES modules by default now. CommonJS is still fully supported, but you have to tell TypeScript you're using it.
  • Fix for CJS: Explicitly set "module": "commonjs" and "moduleResolution": "nodenext".
  • Fix for ESM: Set "module": "nodenext" and "moduleResolution": "nodenext".

moduleResolution — The Practical Push to Bundler

  • Question: How is my code actually being packaged for production?
  • Context: Because TS 6 flipped the default module setting to esnext, the compiler often automatically infers "bundler" for your resolution strategy. Just keep in mind that legacy "node" (Node10) and "classic" are deprecated and removed.
  • Fix: If you are building for the browser using modern tools (Vite, Next.js, ESBuild), explicitly set "moduleResolution": "bundler". If you are writing modern Node.js apps, stick to "nodenext".

rootDir Changed — Broken Output Folders

  • Question: Was my rootDir explicitly set, or was I relying on TS compiler magic?
  • Context: TS used to infer your common source root based on file locations. Now, it defaults strictly to the tsconfig.json folder. If you rely on outDir, your build output might shift from dist/ to dist/src/.
  • Fix: Set "rootDir": "./src" explicitly.

3. Removed Features: Hard Errors Ahead

Deprecation warnings from TS 5.x are gone. These are now hard compiler errors.

  • module Foo {} is dead: You must use namespace Foo {}. The module keyword here conflicts with an upcoming ECMAScript proposal.
  • Dead Module Formats: AMD, UMD, and SystemJS are stripped out. Let your modern bundler handle the format.
  • ES5 Target Deprecated: Targeting "es5" triggers a deprecation error. Modern browsers, Node, Bun, and Deno support ES2020+ natively. Time to move on.
  • baseUrl is Deprecated: It is now strongly recommended to use paths with an explicit rootDir instead.
  • Import Assertions: The syntax changed. The migration tool handles this, but here is what it looks like manually:
// Old (Error in TS 6.0)
import config from "./config.json" assert { type: "json" };
 
// New
import config from "./config.json" with { type: "json" };

4. The Good Stuff: New Features Worth Adopting

It isn’t all pain. TS 6.0 brings features I highly recommend adopting to future-proof your architecture.

  • --stableTypeOrdering (Use with caution): This flag forces union type output to match TS 7.0's deterministic order, surfacing differences before the Go compiler drops. Warning: This is a temporary diagnostic tool only, not meant to stay in production. The TypeScript team notes it can cause up to a 25% slowdown on type-checking. My advice? Enable it locally to find and fix differences, then remove it before 7.0 ships.
  • isolatedDeclarations is Stable: If you run a monorepo, enable "isolatedDeclarations": true alongside "declaration": true. It parallelizes .d.ts generation without invoking the full compiler.
  • Native #/ Subpath Imports: TS now natively resolves #/ imports defined in package.json with full IDE support. Time to delete those custom path aliases.
  • ES2025 and Temporal API: Native Set methods and the Temporal API are here. This adds Temporal API types and built-in TypeScript support, plus enhanced Date-like ergonomics (though it is not a wholesale Date replacement). Opt in via "lib": ["esnext", "esnext.temporal", "dom"].
// 1. ES2025 Native Set Intersections
const frontendDevs = new Set(["Alice", "Bob"]);
const backendDevs = new Set(["Bob", "Charlie"]);
 
// 'Bob' - No more manual loops required!
const fullstackDevs = frontendDevs.intersection(backendDevs);
 
// 2. Temporal API (Timezone safe, nanosecond precision)
const now = Temporal.Now.zonedDateTimeISO();
const nextWeek = now.add({ days: 7 });

5. Tooling Compatibility & Time Expectation

If you are on a modern project targeting ES2020+ using Vite or esbuild, my experience is this migration takes under an hour. If you are sitting on a legacy ES5 project with AMD modules and a messy baseUrl hack... clear your afternoon.

Check your tooling matrix. typescript-eslint, Angular, and Next.js all have compatibility guides for TS 6.0.

6. The Big Picture: Why This Matters for TS 7.0

TS 6.0 is the bridge. Project Corsa (TS 7.0) is clocking in at roughly 10x faster (VS Code’s 1.5M line codebase went from a 77.8s compile to 7.5s in Go). With the 7.0 Beta already out (as of April 20, 2026) and stable coming in June, you don’t have long.

Deprecations are transitionary. Removing them early reduces friction for TS 7.0. Do the work now, set your strict defaults, and when 7.0 drops, you'll be the one drinking coffee while everyone else is putting out fires.


I’m generally the kind of person who likes to dive deep into technology, understand it from the inside, and get better at it. When I find something that clarifies a tricky upgrade or saves time, I share it in the hope it helps others move a bit faster and worry a bit less about the tooling.

References & Further Reading

Me Contacter

Intéressé par une collaboration ? N'hésitez pas à me contacter via l'un des moyens suivants :