TypeScript 6.0 Just Dropped — The Last Major Release Before Everything Changes
TypeScript 6.0 is here with strict-by-default, ES2025 support, Temporal API types, and a wave of hard deprecations. But the real story is what comes next — TypeScript 7.0, rewritten in Go, is months away. Here is a full technical breakdown with code examples and migration paths.
TypeScript 6.0 shipped on March 23 and it is not just another point release. This is the transition release — the one that prepares your codebase for TypeScript 7.0, which is being rewritten in Go and is described by the TypeScript team as "extremely close to completion."
If you are running a TypeScript project of any size, you need to pay attention to this one. The new defaults alone will break most existing tsconfig files. I am going to walk through every significant change with real code examples and the exact configuration fixes you need.
The Defaults Have Flipped
This is the change that will hit the most projects immediately. TypeScript 6.0 changes several critical compiler defaults all at once:
- strict is now true by default — no more opting in to type safety
- module is now esnext instead of commonjs
- target is now es2025 — it floats to the latest year going forward
- noUncheckedSideEffectImports is now true
- types defaults to an empty array instead of enumerating all @types packages
- rootDir now defaults to the tsconfig.json directory instead of being inferred from your source files
The types Default Is the Big One
Previously, TypeScript would automatically scan every @types package in your node_modules and include them all. Now it includes nothing by default. The moment you upgrade, you will see errors like this:
Cannot find module '...' or its corresponding type declarations.
Cannot find name 'fs'. Do you need to install type definitions for node?
Try `npm i --save-dev @types/node` and then add 'node' to the types field.
Cannot find name 'describe'. Try installing @types/jest or @types/mocha.The fix is straightforward. Explicitly list the type packages your project needs:
{
"compilerOptions": {
"types": ["node", "jest"]
}
}If you want to restore the old behaviour temporarily, you can set types to ["*"]. But do not do that long term — the TypeScript team says projects that set their types correctly saw 20 to 50 percent build time improvements. Scanning every @types package was always wasteful.
The rootDir Default Change
This one is subtle but will trip up projects where the tsconfig.json lives in a different directory than the source code. Previously TypeScript inferred rootDir from your source files. Now it defaults to the directory containing tsconfig.json.
If your project has a structure where your config is at the root but your code is in a src directory, you need to set rootDir explicitly:
{
"compilerOptions": {
"rootDir": "./src"
},
"include": ["./src"]
}For monorepo setups where files live outside the config directory, you might need a relative parent path:
{
"compilerOptions": {
"rootDir": "../src"
},
"include": ["../src/**/*.tests.ts"]
}Hard Deprecations — These Will Break Your Build
TypeScript 6.0 introduces hard deprecations for a long list of legacy options. These are not warnings — they are errors unless you explicitly set ignoreDeprecations in your tsconfig:
{
"compilerOptions": {
"ignoreDeprecations": "6.0"
}
}That is a temporary escape hatch, not a solution. TypeScript 7.0 will remove these entirely. Here is every major deprecation and exactly how to migrate.
target: es5 Is Gone
The lowest target is now ES2015. If you still need ES5 output for legacy browser support, you need an external compiler like Babel or esbuild as a downstream step. TypeScript will no longer do the downleveling for you.
The --downlevelIteration flag is also deprecated since it only existed for the ES5 target.
--moduleResolution node Is Deprecated
The old node resolution strategy (also known as node10) is deprecated. Migrate to nodenext for Node.js projects or bundler if you use Webpack, Vite, or esbuild:
// Before
{
"compilerOptions": {
"moduleResolution": "node"
}
}
// After — pick one
{
"compilerOptions": {
"moduleResolution": "nodenext"
}
}
// OR for bundler-based projects
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}A new combination is also now supported: --moduleResolution bundler with --module commonjs. This was previously blocked and is useful for projects migrating incrementally away from the deprecated node resolution.
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "bundler"
}
}--baseUrl Migration
Using baseUrl as a module lookup root is deprecated. The fix is to add the base path directly into your paths entries:
// Before
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@app/*": ["app/*"],
"@lib/*": ["lib/*"]
}
}
}
// After
{
"compilerOptions": {
"paths": {
"@app/*": ["./src/app/*"],
"@lib/*": ["./src/lib/*"]
}
}
}If your codebase relies on baseUrl for bare import resolution (importing from arbitrary directories without a path prefix), you can restore that behaviour with a wildcard path entry:
{
"compilerOptions": {
"paths": {
"*": ["./src/*"],
"@app/*": ["./src/app/*"],
"@lib/*": ["./src/lib/*"]
}
}
}esModuleInterop Is Now Always On
You can no longer set esModuleInterop or allowSyntheticDefaultImports to false. Safer interop is permanently enabled. If you have imports written for the old behaviour, update them:
// Before (with esModuleInterop: false)
import * as express from "express";
// After (always enabled in TypeScript 6.0+)
import express from "express";Legacy Module Syntax
The old module keyword for namespaces is deprecated. Use namespace instead:
// Deprecated in TypeScript 6.0
module Foo {
export const bar = 10;
}
// Correct syntax
namespace Foo {
export const bar = 10;
}
// Ambient module declarations still work fine
declare module "some-module" {
export function doSomething(): void;
}Import Assertions to Import Attributes
The asserts keyword on imports is deprecated. Replace with the with keyword:
// Deprecated
import blob from "./data.json" asserts { type: "json" };
// Correct syntax
import blob from "./data.json" with { type: "json" };Other Deprecated Options
- --module amd, umd, systemjs, and none are all deprecated. Use ESM with a bundler.
- --outFile is deprecated. Use Webpack, esbuild, Vite, or Rollup for bundling.
- --moduleResolution classic is removed.
- --alwaysStrict false is no longer allowed. All code is assumed to be in strict mode.
- /// <reference no-default-lib="true"/> is removed. Use --noLib or --libReplacement instead.
Command Line Behaviour Change
If you have a tsconfig.json in your project and try to compile a specific file directly, TypeScript 6.0 will now error:
# This now throws an error
tsc foo.ts
# error TS5112: tsconfig.json is present but will not be loaded
# Use --ignoreConfig to compile individual files
tsc --ignoreConfig foo.tsES2025, Temporal, and New JavaScript APIs
On the positive side, TypeScript 6.0 adds proper support for the latest JavaScript features that have reached Stage 4.
Temporal API Types
Temporal is the Stage 4 replacement for JavaScript's notoriously broken Date object. If you have ever written date manipulation code and felt like you were fighting the language, Temporal is the fix. TypeScript 6.0 gives you full type support out of the box:
let yesterday = Temporal.Now.instant().subtract({
hours: 24,
});
let tomorrow = Temporal.Now.instant().add({
hours: 24,
});
console.log(`Yesterday: ${yesterday}`);
console.log(`Tomorrow: ${tomorrow}`);No more date-fns or Luxon for basic date arithmetic. Temporal handles time zones, durations, and calendar math correctly by design.
Map and WeakMap Upsert Methods
New getOrInsert and getOrInsertComputed methods eliminate one of the most common boilerplate patterns in JavaScript — checking if a map key exists, inserting a default if not, and returning the value:
// Before — verbose check-and-set pattern
function processOptions(compilerOptions: Map<string, unknown>) {
let strictValue: unknown;
if (compilerOptions.has("strict")) {
strictValue = compilerOptions.get("strict");
} else {
strictValue = true;
compilerOptions.set("strict", strictValue);
}
}
// After — single line with getOrInsert
function processOptions(compilerOptions: Map<string, unknown>) {
let strictValue = compilerOptions.getOrInsert("strict", true);
}For expensive default values that you only want to compute if the key is missing, use getOrInsertComputed:
someMap.getOrInsertComputed("someKey", () => {
return computeSomeExpensiveValue();
});RegExp.escape
A small but welcome addition. You can now safely escape user input for use in regular expressions without third-party libraries:
function matchWholeWord(word: string, text: string) {
const escapedWord = RegExp.escape(word);
const regex = new RegExp(`\\b${escapedWord}\\b`, "g");
return text.match(regex);
}DOM Library Consolidation
The contents of lib.dom.iterable.d.ts and lib.dom.asynciterable.d.ts have been merged into lib.dom.d.ts. If your tsconfig previously needed to include dom.iterable separately, you can remove it:
// Before — needed both
{
"compilerOptions": {
"lib": ["dom", "dom.iterable"]
}
}
// After — just dom works
{
"compilerOptions": {
"lib": ["dom"]
}
}Iterating over DOM collections like NodeList and HTMLCollection just works now without the extra library inclusion.
Better Type Inference for this-less Functions
TypeScript 6.0 improves how generic type inference works with method syntax in object literals. Previously, if you used method shorthand syntax instead of arrow functions, TypeScript would lose the generic context and fail to infer parameter types:
declare function callIt<T>(obj: {
produce: (x: number) => T,
consume: (y: T) => void,
}): void;
// Arrow syntax — always worked
callIt({
produce: (x: number) => x * 2,
consume: y => y.toFixed(),
});
// Method syntax — now works in TypeScript 6.0
callIt({
consume(y) { return y.toFixed(); },
produce(x: number) { return x * 2; },
});This is a subtle change but it fixes a long-standing pain point in functional patterns. If you have ever wondered why TypeScript lost type information when you switched from arrow to method syntax, this is the fix.
Subpath Imports with #/
Node.js 20+ supports subpath imports in package.json using the # prefix. TypeScript 6.0 now supports a simpler form that aligns with bundler conventions:
// Before — required intermediate path segment
{
"name": "my-package",
"type": "module",
"imports": {
"#root/*": "./dist/*"
}
}
// After — TypeScript 6.0 supports #/ directly
{
"name": "my-package",
"type": "module",
"imports": {
"#/*": "./dist/*"
}
}Usage in your code:
import * as utils from "#/utils.js";This is a cleaner import pattern that avoids the arbitrary intermediate path names. If you are using a bundler that already supports this convention, TypeScript will now resolve it correctly.
Preparing for TypeScript 7.0
The real headline is not in 6.0 itself — it is what 6.0 is preparing you for. TypeScript 7.0 is a native port written in Go. The TypeScript team says it is months away, not years.
TypeScript 6.0 includes a --stableTypeOrdering flag specifically designed to help you test compatibility with 7.0. When enabled, TypeScript 6.0 will produce type output in the same deterministic order that 7.0 uses. This matters because 7.0's parallel type-checking produces different (but equivalent) type ordering compared to 6.0.
{
"compilerOptions": {
"stableTypeOrdering": true
}
}If you encounter type errors with this flag enabled, the fix is usually to add explicit type annotations where TypeScript was previously relying on inference order:
// If stableTypeOrdering causes errors, add explicit types
// Add explicit type arguments
- someFunctionCall(/*...*/);
+ someFunctionCall<SomeExplicitType>(/*...*/);
// Or annotate variables
- const someVariable = { /*...*/ };
+ const someVariable: SomeExplicitType = { /*...*/ };The flag adds up to 25 percent overhead, so do not leave it on in production builds. But run your CI with it enabled periodically. When 7.0 lands, you want zero surprises.
You can already test TypeScript 7.0 directly via the @typescript/native-preview npm package or the VS Code extension from the TypeScript team.
The Full Migration Checklist
If you are upgrading a project to TypeScript 6.0, here is the exact order I would do it:
- Install TypeScript 6.0: npm install -D typescript@latest
- Run tsc and read every error before changing anything.
- Add explicit "types" to your tsconfig — start with ["node"] for Node.js projects, add "jest" or "mocha" if you use them. This alone could speed up your builds by 20 to 50 percent.
- Set "rootDir" explicitly if you were relying on inference. Check your output directory structure after the change.
- Migrate --moduleResolution node to nodenext or bundler.
- Migrate --baseUrl to explicit paths prefixes.
- Update any import assertions (asserts) to import attributes (with).
- Replace module keywords with namespace where needed.
- Update default imports if you had esModuleInterop set to false.
- Remove "dom.iterable" from your lib array if present.
- Run your full test suite with --stableTypeOrdering enabled to check for TypeScript 7.0 compatibility.
- Remove ignoreDeprecations if you added it — make sure every deprecation is actually resolved.
The window between TypeScript 6.0 and 7.0 is explicitly short. The team is telling you to prepare now. Listen to them.
TypeScript 6.0 is not the destination. It is the bridge. The real performance and architectural leap is TypeScript 7.0 — and if you do the work now, the transition will be painless.
Need Help Upgrading?
At Tally Digital, every client project we build runs on TypeScript. We have already been through the 6.0 migration on our own codebases and know exactly where the rough edges are. If you are staring at a wall of deprecation warnings and do not want to spend two days untangling legacy tsconfig options, book a call and we will scope the migration for you.
Share this article