Back to all posts

cat posts/typescript-5-8-whats-worth-adopting-first.md --category "TypeScript" --views 901

TypeScript 5.x to 7.0: What Breaks and What It Buys

TypeScript 7.0 is a native Go port of the compiler, and Microsoft measured type-checking the VS Code codebase falling from 125.7 seconds to 10.6 seconds at default settings. A team still on 5.8 is two majors behind, so this is the staged route through 6.0, the changed defaults and removals that actually break builds, and who should wait for the 7.1 API.

  • --author By Falak Mahmood
  • --date January 22, 2026
  • --read 13 min read
  • --views 901 views

Updated 9 October 2026. This post was originally a feature-adoption guide for TypeScript 5.8. TypeScript 6.0 shipped on 23 March 2026 and 7.0 on 8 July 2026, so it has been rewritten as a migration guide for teams still on the 5.x line. One unsourced performance figure from the original has been removed.

If your repository still pins TypeScript 5.8, the useful question is no longer which compiler flags are worth switching on. TypeScript 7.0 is a native port of the compiler and language service to Go. Microsoft measured type-checking the VS Code codebase dropping from 125.7 seconds to 10.6 seconds at default settings, and the release sitting between you and that number, 6.0, exists largely to tell you what will break.

Where TypeScript Actually Is Now

Two majors have landed since the 5.x line. TypeScript 6.0 arrived on 23 March 2026, and its announcement is unusually direct about the purpose: "TypeScript 6.0 is a unique release in that we intend for it to be the last release based on the current JavaScript codebase."

TypeScript 7.0 followed on 8 July 2026: the compiler and language service ported to Go, compiled to a native binary, released as the default typescript package. The npm registry serves 7.0.2 as latest on 9 October 2026. The published 7.1 iteration plan sets 6 October 2026 for beta, 10 November for the release candidate and 24 November 2026 for stable.

So a 5.8 codebase is not one upgrade behind. It is one deprecation release and one architectural change behind, and those two things want handling separately.

Why 6.0 Is the Staging Step, Not a Detour

The temptation from 5.8 is to skip straight to 7.0 and deal with whatever falls out. Resist it, for one reason: 6.0 deprecated precisely the things 7.0 removes. Its deprecation list reads like the 7.0 breaking-change list written a quarter early, covering target: es5, --moduleResolution node, --baseUrl, the amd, umd and systemjs module formats, --outFile, the legacy module namespace syntax, esModuleInterop: false, allowSyntheticDefaultImports: false, and the old import assertion syntax.

That is the point. On 6.0 those settings still work and emit warnings, and there is an explicit escape hatch: options deprecated in 6.0 "will continue to work without errors when "ignoreDeprecations": "6.0" is set." You get a machine-generated inventory of your 7.0 migration work while the build stays green. On 7.0 the same settings are hard errors with no equivalent valve.

Treat the 6.0 warning log as the backlog. Clear it, then move.

The Performance Story, With Attribution

The headline Microsoft has used since announcing the native port is 10x: the original announcement promised to "drastically improve editor startup, reduce most build times by 10x, and substantially reduce memory usage." The 7.0 release post publishes the measurements behind it. These are Microsoft's own numbers on open-source codebases at default settings.

Codebase Before TypeScript 7.0 Speedup
VS Code125.7s10.6s11.9x
Sentry139.8s15.7s8.9x
Bluesky24.3s2.8s8.7x
Playwright12.8s1.47s8.7x
tldraw11.2s1.46s7.7x

The spread is roughly 7.7x to 11.9x, which is a better planning figure than the round 10x. Two mechanisms produce it. One is native code instead of JavaScript on Node. The other is parallelism: 7.0 "brings native code speed, shared memory multithreading, and a number of new optimizations" and "now performs many steps in parallel, including parsing, type-checking, and emitting." The default is four type-checking workers, configurable with the new --checkers flag, and Microsoft reports that at --checkers 8 larger codebases reached speedups between 10.6x and 16.7x, at the cost of more memory. At default settings memory went the other way, down 6 to 26 percent across the tested projects.

Editor latency improves more than the build numbers suggest, and that is where developers feel the compiler. Opening a file with an error in the VS Code codebase previously took about 17.5 seconds to show the first error. Under TypeScript 7 it is "under 1.3 seconds", described as over 13x faster.

Now the part that keeps the claim honest. None of this touches your runtime. Shipped JavaScript executes at the same speed, because the output is the same output. Your bundler does not get faster unless it was using the compiler to type-check, and most are not: esbuild, SWC and Vite strip types without checking them. Dependency installation, Docker layer builds and container pushes are unchanged. What gets faster is tsc, tsc --build and the language service. In a typical pipeline that is one stage, so size the win by how many seconds that stage costs you today.

What Does Not Change

This is what makes a two-major jump less alarming than it reads. The Go implementation was not a reimagining of the type system. Microsoft described the approach from the start as a port of the existing code: "By porting the current codebase, we expect to be able to preview a native implementation." The checker was mirrored structurally rather than redesigned, and the 7.0 announcement states the consequence plainly: projects that compile cleanly with TypeScript 6.0 "should compile identically in TypeScript 7.0" for type-checking and command-line behaviour.

So this migration carries configuration risk, not inference risk. You are not expecting a wave of new errors from a stricter checker. You are expecting a compiler that refuses to start because of options in your tsconfig.json, which is a different kind of problem: enumerable, immediate, and not hiding in a rarely-executed branch of a generic utility type.

One semantic change is worth knowing about. Template literal type inference now handles Unicode by code point rather than by UTF-16 code unit, so an emoji or other multi-byte character is treated as a single unit instead of a surrogate pair. If you have string-manipulation types operating on user-facing text, check them. Most codebases have none.

The Changed Defaults

TypeScript 7.0 changed what happens when you say nothing. Each of these is an improvement in isolation and a surprise in a migration, so the remedy is the same: pin the value you currently rely on, explicitly, before you upgrade.

Option Was Now What to do
strictfalsetrueSet it true on 5.8 first and fix the fallout there, or pin false and schedule it
modulecommonjsesnextState the value your runtime needs; CommonJS output does not happen by accident any more
typesall @types packagesempty arrayList every global type package you depend on
rootDirinferredproject rootSet it to ./src if your config sits above your sources
noUncheckedSideEffectImportsfalsetrueAdd declarations for CSS, asset or polyfill side-effect imports
targetes5 unless setlatest stable before esnextPin an explicit ES year so your output does not drift with compiler releases

The types change deserves the most attention, and it hurts enterprise monorepos out of proportion to its size. Until 7.0, every @types package anywhere in node_modules was pulled into the global scope of every compilation, which is why describe and process were simply available and nobody thought about it. Nothing is auto-included now. List what you need:

{
  "compilerOptions": {
    "types": ["node", "jest"]
  }
}

In a single-package repository that is a two-minute fix. In a workspace with thirty packages, each with its own tsconfig.json extending a shared base, you have to decide per package which globals are legitimate. Do that work on 5.8 rather than during the migration: it is identical work, and the error messages are easier to read when only one thing has changed.

The Removals

Removals are blunter than default changes. The compiler rejects the option and stops.

  • baseUrl is gone. Microsoft's remediation is that paths "can be updated to be relative to the project root instead of baseUrl". This is the second enterprise monorepo tax, because baseUrl plus bare-specifier aliases was the standard way to wire internal packages before workspaces matured. Every alias has to be rewritten relative to the project root, and everything else reading those mappings has to agree: bundler config, Jest moduleNameMapper, ESLint import resolver. Fix the tsconfig.json alone and you get a build that type-checks and fails at runtime.
  • target: es5 is gone, and the announcement offers no replacement. If you still support browsers that need ES5, the compiler is no longer where that happens. Downlevel in your bundler or a dedicated transpiler step and let TypeScript emit a modern target. Decide this before the migration, because it changes your output pipeline rather than your config.
  • downlevelIteration is unsupported, which follows from the above. It existed to make iteration protocols work on old targets.
  • amd, umd, systemjs and none module formats are gone. Library authors shipping UMD bundles for script-tag consumers need a bundler for that artifact.
  • moduleResolution values node, node10 and classic are gone. Move to nodenext or bundler. Expect this one to surface genuine import mistakes, since the older algorithms were forgiving about extensions and directory indexes.
  • esModuleInterop, allowSyntheticDefaultImports and alwaysStrict can no longer be disabled. If any is false in your config today, import shapes in your code change when it flips.
  • The legacy module namespace keyword is disallowed, and import assertions use with rather than assert.
  • stableTypeOrdering is enforced and cannot be switched off, which can change how union types read in error messages and emitted declarations. Snapshot tests over declaration output need regenerating.

The API Gap: Who Has to Wait

This is the gating constraint for a large share of projects, and it has nothing to do with your own code. TypeScript 7.0 "does not ship with an API", and the RC announcement was explicit: "We won't have a stable programmatic API available until at least several months from now with TypeScript 7.1."

That matters because an entire tier of tooling does not run tsc over .ts files. It embeds the compiler to type-check something else: a template, a component block, a markup dialect. The 7.0 announcement names the casualties. Tools "which embed TypeScript into their own compilers and language services can only currently rely on TypeScript 6.0", so "workflows that use Vue, MDX, Astro, Svelte, and others will likely not yet be able to leverage TypeScript 7", and "specialized type-checking within templates like Angular will also likely not use TypeScript 7".

Angular projects can split the difference, using "TypeScript 7 to get fast project-wide error detection at the CLI with tsc, and TypeScript 6.0 for editor support". For everyone else the guidance is to stay on 6.0 for now. A compatibility package, @typescript/typescript6, exposes the 6.0 APIs alongside the native compiler, which is what lets a mixed setup exist.

The same applies to anything in-house importing the compiler API: a codemod, a type-aware lint rule, a documentation generator, a TypeScript server plugin, a build script calling ts.createProgram. Inventory those before you pick a date. The 7.1 iteration plan lists the Content Mapper API, Emit API and Language Service API as stabilisation targets, with stable scheduled for 24 November 2026.

The Upgrade Sequence

Five steps, each landing as its own pull request. The discipline is that every step keeps the build green before the next one starts.

  • 1. Harden the 5.8 config in place. Before touching the compiler version, make your tsconfig.json state its assumptions out loud: explicit target, explicit module, "types": [] plus the list you need, explicit rootDir, and strict set to whatever you can defend. Every 7.0 default change then becomes a no-op. This step does most of the work.
  • 2. Move to 6.0 and read the warnings. Upgrade the package, run a full tsc --build, capture the deprecation output, and fix nothing in this PR. That log is your scope document, and "ignoreDeprecations": "6.0" keeps CI passing while you work through it.
  • 3. Clear the deprecations. Path aliases off baseUrl, module resolution to nodenext or bundler, legacy module formats out, ES5 downleveling relocated to the bundler if you still need it. Each is independently reviewable and each is an improvement whether or not you reach 7.0.
  • 4. Run 7.0 in parallel in CI. Add a non-blocking job that installs 7.0 and runs tsc --noEmit. You get the real error list against the real repository without betting the pipeline on it, and since semantics are intended to match 6.0, a short list means you are nearly done.
  • 5. Switch, with the API constraint respected. Flip the dependency when the parallel job is clean and nothing in your stack needs the compiler API. If something does, keep 6.0 for the editor and framework toolchain and revisit after 7.1 stable.

Where this needs to be scoped work rather than a background chore, it is how we run a TypeScript modernization engagement.

Measuring the CI Win on Your Own Repository

Build minutes are a real line item on Azure Pipelines and GitHub Actions, and type-checking is often a bigger share of them than anyone has checked. Rather than apply someone else's multiple to your pipeline, measure the one stage that changes. It takes ten minutes.

# How long does type-checking alone cost you today?
# Run on a cold cache, the way CI does.
rm -rf node_modules/.cache tsconfig.tsbuildinfo
/usr/bin/time -f "%e s wall, %M KB peak" npx tsc --build --force

# Then the same against 7.0 in a scratch checkout.
npm i -D typescript@7
/usr/bin/time -f "%e s wall, %M KB peak" npx tsc --build --force

# Worth a second pass with more workers on a large repo.
npx tsc --build --force --checkers 8

Multiply the delta by your merge frequency and your runner rate. Then add the part that never appears on an invoice: if the type-check stage takes two minutes, every author waits two minutes to learn they broke something, several times a day.

One caution. More checker workers want more memory, so if your agents are small hosted runners, measure peak resident size alongside wall time before raising --checkers, or you trade a timeout for an out-of-memory kill.

Decision Guide

Codebase profile Move Why
Node or React service, bundler-based, no template type-checkingGo to 7.0 now, via 6.0Nothing in the stack needs the compiler API; the work is config only
Large monorepo on baseUrl aliases and implicit @typesStage via 6.0 over a few sprintsAlias rewrites and per-package types lists are the whole cost, and they are safer done one at a time
Vue, Svelte, Astro or MDX in the buildLand on 6.0, wait for 7.1Volar and equivalents can only rely on the 6.0 API until the new one stabilises
Angular with template type-checkingSplit: 7.0 at the CLI, 6.0 in the editorMicrosoft documents this combination as the supported interim arrangement
Still shipping ES5 for legacy browsersRelocate downleveling firsttarget: es5 and downlevelIteration are gone with no compiler-side replacement
In-house codemods, lint rules or plugins on the compiler APIWait for 7.1, or bridge with @typescript/typescript67.0 ships no API; Content Mapper, Emit and Language Service APIs are 7.1 targets
Library published to npm with generated declarationsGo to 7.0, regenerate snapshotsEnforced stable type ordering can change declaration and error text

What To Do This Week

  • Grep your configs for baseUrl, target, moduleResolution and esModuleInterop. That search result is most of your migration cost.
  • Add "types": [] to your base config on 5.8 and fix what breaks now, while only one thing has changed.
  • List every dependency and internal script importing the compiler API. That list, not the compiler, decides whether you can move in 2026.
  • Time your type-check stage on a cold cache and write the number down.

subscribe # the AI news that matters, minus the noise

Book a Call

Sources

Tags

Related posts