TypeScript 6.0 landed in March 2026, and unlike most minor-version releases, this one has teeth. It’s the last version of the compiler written in JavaScript — version 7.0 will be a full rewrite in Go — and the team used this moment to clean house on a decade’s worth of accumulated defaults and legacy options.
The new features are genuinely good. The breaking changes are real and will catch teams off guard. The defaults shift is the kind of thing that silently breaks tsc --noEmit in CI at 2am on a Tuesday.
Let’s go through all of it without hand-waving.
Why 6.0 Is Different From a Typical Release
Most TypeScript releases follow a comfortable pattern: new type-level features, stricter inference, maybe a new utility type. You upgrade, run the compiler, fix fifteen unknown errors, ship it.
TypeScript 6.0 breaks that pattern. It’s explicitly a bridge release — its job is to deprecate or remove everything that TypeScript 7.0 (the Go-based native compiler) won’t support. The team gave developers a release cycle to adjust before 7.0 yanks the rug entirely.
The signal here: if you’re still using --moduleResolution node, --outFile, --target es5, or any AMD/UMD modules, TypeScript 6.0 is warning you. TypeScript 7.0 will not warn you — it will just refuse.
Official GitHub: microsoft/TypeScript
New Features Worth Actually Using
Less Context-Sensitivity on this-less Functions
This one has been a long-standing papercut. In previous TypeScript versions, method syntax in object literals could confuse generic inference depending on property order. The following would fail if consume came before produce:
declare function callIt<T>(obj: {
produce: (x: number) => T,
consume: (y: T) => void,
}): void;
// Before 6.0: would fail with method syntax if properties were out of order
callIt({
consume(y) { return y.toFixed(); }, // inferred 'y: number' correctly now
produce(x: number) { return x * 2; },
});
The fix: functions that don’t use this are no longer treated as contextually sensitive during inference. The ordering of properties no longer breaks type inference. This is small but it removes an entire class of "why does this fail when I swap these two lines" confusion.
Temporal API Types — Finally
The Temporal proposal hit Stage 4, and TypeScript 6.0 ships built-in types for it. No more @types/temporal-polyfill or hand-rolling declarations.
const now = Temporal.Now.instant();
const tomorrow = now.add({ hours: 24 });
const yesterday = now.subtract({ hours: 24 });
// Duration, ZonedDateTime, PlainDate — all typed
const meeting = Temporal.PlainDateTime.from("2026-06-01T09:00");
Browser support is still uneven, so you’ll need a polyfill in most environments. But having the types officially in the compiler means your IDE tooling just works.
Map/WeakMap getOrInsert and getOrInsertComputed
The "upsert" pattern for Maps has always been verbose. TypeScript 6.0 types the new ECMAScript proposal methods:
// Old pattern — annoying boilerplate
let val: string;
if (cache.has(key)) {
val = cache.get(key)!;
} else {
val = computeDefault();
cache.set(key, val);
}
// TypeScript 6.0 — typed and clean
const val = cache.getOrInsert(key, computeDefault());
// Lazy version for expensive computations
const val = cache.getOrInsertComputed(key, (k) => expensiveOp(k));
These live in the esnext lib target, so set "lib": ["esnext"] or higher to get them.
RegExp.escape()
You’ve either written a escapeRegExp utility function a dozen times, or you’ve copied it from Stack Overflow. Both are now unnecessary:
function highlight(term: string, text: string) {
const pattern = new RegExp(RegExp.escape(term), "gi");
return text.replace(pattern, `<mark>$&</mark>`);
}
Also lives in esnext lib.
ES2025 Target and Lib
"target": "es2025" is now a valid option and is the new default. It brings Promise.try, Iterator methods, Set operations (union, intersection, difference), and RegExp.escape into the typed surface area.
Subpath Imports With #/
Node.js added a shorthand for subpath imports — instead of defining "#root/*": "./dist/*", you can map #/* directly:
// package.json
{
"imports": {
"#/*": "./dist/*.js"
}
}
// your code
import { logger } from "#/utils/logger.js";
TypeScript 6.0 resolves these correctly under nodenext and bundler module resolution. This is cleaner than path aliases and works natively with Node’s module system.
DOM iterable Merged Into dom
Previously, if you wanted to iterate DOM collections, you needed:
"lib": ["dom", "dom.iterable"]
In TypeScript 6.0, dom already includes iterable and async-iterable content. Your querySelectorAll results, NodeList, HTMLCollection — all iterable by default. Simplifies tsconfig for browser projects.
--moduleResolution bundler + --module commonjs
Previously a forbidden combination. Now allowed, specifically to give teams a migration path off deprecated --moduleResolution node. You can keep CJS output while getting bundler-style module resolution semantics during the transition period.
The Defaults Changed. This Will Break Things.
Here’s the table that’s going to generate the most Stack Overflow questions over the next six months:
| Option | Old Default | New Default |
|---|---|---|
strict |
false |
true |
module |
commonjs |
esnext |
target |
es5 |
es2025 |
types |
all @types/* |
[] (none) |
rootDir |
inferred from files | . (tsconfig directory) |
noUncheckedSideEffectImports |
false |
true |
libReplacement |
true |
false |
If your project has an explicit tsconfig.json with all these set already, you’re probably fine. If you’ve been relying on implicit defaults anywhere — especially strict, module, or types — you need to audit.
The types: [] Default Is the Silent Killer
This is the one that will catch the most people. TypeScript previously auto-discovered all installed @types/* packages and included them globally. That’s how process.env.NODE_ENV just worked without you ever setting "types": ["node"] in your tsconfig.
In TypeScript 6.0, types defaults to an empty array. You must declare your global type packages explicitly:
{
"compilerOptions": {
"types": ["node", "jest", "react"]
}
}
The upside is real: projects that tested this saw 20–50% build time improvements because the compiler was no longer ingesting hundreds of @types packages it didn’t need. The downside: if you don’t add this, code that referenced global types from unrelated packages will start throwing errors.
Gotcha: Monorepos are particularly exposed here. Each workspace tsconfig needs its own types list. Don’t just fix the root.
What’s Deprecated (And Gone in 7.0)
These aren’t soft deprecations. Each of these will be completely removed in TypeScript 7.0. You can silence the warnings now with "ignoreDeprecations": "6.0", but that setting won’t work in 7.0 — at that point, your build simply breaks.
target: es5 — Dropped
The minimum supported target is now ES2015. If you’re still targeting IE11 or ancient Node versions, TypeScript 6.0 will warn; TypeScript 7.0 will error.
What to do: Use esbuild, Babel, or SWC as a second pass to downlevel to ES5 if you genuinely need it. TypeScript was never great at this anyway — the output was verbose and slow compared to dedicated transpilers.
--moduleResolution node (also called node10) — Deprecated
This is the resolution mode everyone learned TypeScript with. It’s been misleading for years — it doesn’t match how modern Node.js actually resolves modules. Time’s up.
Migration path:
- Node.js projects: switch to
--moduleResolution nodenextand set--module nodenext - Bundler projects (Webpack, Vite, Rollup): switch to
--moduleResolution bundler
// For Node.js applications
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext"
}
}
// For bundler-based (Vite, Webpack, etc.)
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
}
}
--module amd, umd, systemjs, none — Gone
AMD and UMD are legacy module formats from the RequireJS era. SystemJS had a brief moment of relevance with Angular. None of them matter in 2026.
If you’re somehow still emitting AMD or UMD, TypeScript is not the right tool to produce that output — use a bundler that’s purpose-built for it.
--outFile — Gone
--outFile concatenated your TypeScript output into a single file. It only worked with AMD/SystemJS, which are also gone. Use a real bundler.
--baseUrl as Module Resolution Root — Deprecated
baseUrl was a common hack to get import { foo } from "utils/bar" to resolve relative to src/. It worked by making the baseUrl a resolution root.
That behavior is gone. Use paths with explicit mapping instead:
// Before
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@app/*": ["app/*"]
}
}
}
// After
{
"compilerOptions": {
"paths": {
"@app/*": ["./src/app/*"],
"utils/*": ["./src/utils/*"]
}
}
}
baseUrl still works for its documented purpose (resolving relative imports), but using it as an implicit module root is deprecated.
esModuleInterop: false — Deprecated
You can no longer disable esModuleInterop. The safer interop behavior is now always on. The practical consequence: the import * as X from "module" pattern for CommonJS modules should become import X from "module".
// Deprecated pattern (still works with ignoreDeprecations in 6.0)
import * as express from "express";
const app = express();
// Correct pattern
import express from "express";
const app = express();
Gotcha: Some older @types packages only expose a namespace export and don’t have a proper default export. You may hit Module has no default export errors. Check the types and file an issue with upstream if needed.
module Foo {} Namespace Syntax — Deprecated
The module keyword was repurposed early in TypeScript’s history to mean "namespace." It’s now unambiguously just ES modules. Use namespace instead:
// Deprecated
module MyNamespace {
export const version = "1.0.0";
}
// Correct
namespace MyNamespace {
export const version = "1.0.0";
}
Import Assertions (asserts) — Deprecated
The assert import attribute syntax lost the spec race to with. Update your JSON imports:
// ❌ Deprecated
import config from "./config.json" assert { type: "json" };
// ✅ Current
import config from "./config.json" with { type: "json" };
Migration Checklist
Work through this in order. Most projects will hit at least three of these.
// tsconfig.json — minimum you should add before upgrading
{
"compilerOptions": {
"strict": true, // was likely already true in modern projects
"target": "es2020", // or whatever actually matches your runtime
"module": "nodenext", // or "esnext"/"bundler" depending on your setup
"moduleResolution": "nodenext",
"types": ["node"], // add whatever @types packages you actually need
"rootDir": "./src", // be explicit rather than relying on inference
"esModuleInterop": true, // no longer optional anyway
"allowSyntheticDefaultImports": true
}
}
Steps:
- Run
tsc --noEmitand collect all errors before touching config - Add
"ignoreDeprecations": "6.0"to silence deprecation noise while you fix type errors first - Fix type errors from default changes (especially
strict: truenewly applying) - Migrate module resolution
- Add explicit
typesarray - Fix import syntax (
asserts→with,import * as→ default imports where applicable) - Remove
"ignoreDeprecations"and fix remaining deprecation warnings - Run your full test suite
There’s also a community migration tool, ts5to6, that automates some of the mechanical changes.
The --stableTypeOrdering Flag — For the Cautious
This is a diagnostic tool, not a feature you’ll ship with. Adding --stableTypeOrdering makes TypeScript 6.0 sort types in the same deterministic order that TypeScript 7.0 will use. It costs about 25% in type-check time.
The point: if you’re comparing .d.ts output between your current codebase and the TypeScript 7.0 native compiler preview, this flag reduces noise in the diff. Most teams won’t need it until 7.0 beta testing starts.
What’s Coming in TypeScript 7.0
TypeScript 7.0 is the Go-based native rewrite. The team has been working on it in parallel and released a preview (@typescript/native-preview on npm). Key points:
- Everything deprecated in 6.0 is removed entirely
- Type-checking is expected to be significantly faster — benchmarks show 5–10x on large codebases
- The API surface will change for tooling authors (language server plugins, transformers)
strict: trueis the only option — there’s no opt-out
If you’re maintaining a TypeScript monorepo or a library that relies on compiler internals, start tracking the 7.0 previews now. The earlier you know what breaks, the less miserable the actual migration is.
TypeScript 6.0 is not a glamorous release, but it’s an honest one. The team used it to do necessary housekeeping that would have been harder to do mid-cycle on 7.0. If you’ve been keeping your tsconfig modern and explicit, the upgrade is straightforward. If you’ve been coasting on implicit defaults from 2018, expect a rough morning.
The trade-off is worth it — the defaults are strictly better, the deprecated options were genuinely dead weight, and the new features (Temporal, getOrInsert, RegExp.escape) reduce real boilerplate. Just don’t upgrade the week before a big release.
Sources:
- Announcing TypeScript 6.0 — Microsoft DevBlogs
- TypeScript 6.0 Release Notes — typescriptlang.org
- TypeScript 5.x to 6.0 Migration Guide — GitHub Gist
- TypeScript 6.0 Ships as Final JavaScript-Based Release — Visual Studio Magazine
- Microsoft Releases TypeScript 6.0, the Last Version Built on JavaScript — Neowin