DEV Community

BeanBean
BeanBean

Posted on • Originally published at nextfuture.io.vn

TypeScript 6.0 Migration Guide: Stricter Defaults, Temporal API, and the Road to 7.0

Originally published on NextFuture

TypeScript 6.0 shipped on March 23, 2026, and it's not just another point release. It's the last major version built on the JavaScript compiler before the team rewrites everything in Go for TypeScript 7.0. That makes 6.0 both a feature release and a farewell — and it introduces enough breaking changes that you can't just bump the version and hope for the best.

I've migrated three production Next.js apps to TypeScript 6.0 over the past week. Here's what actually broke, what got better, and how to prepare your codebase for the Go-native future.

What Makes TypeScript 6.0 Different

Every TypeScript release adds features. This one removes things. The team is clearing the runway for 7.0's Go compiler, which means deprecated options, stricter defaults, and the explicit message: modernize now or break later.

The headline changes fall into four categories:

  • Stricter defaults that will break existing tsconfig.json files

  • Temporal API support — first-class types for the new date/time standard

  • Deprecations of legacy module systems and resolution strategies

  • Performance optimizations that cut build times by 20-50% in monorepos

Breaking Change #1: Strict Mode Is Now Default

This is the change that will trip up the most teams. In TypeScript 6.0, strict: true is the default for new projects. If your tsconfig.json doesn't explicitly set strict, you're now getting strict mode whether you wanted it or not.

For teams already using strict mode (which you should be), nothing changes. But if you've been running without it, you'll see a flood of errors:

// Before TS 6.0: no error without strict
function greet(name) {
  return "Hello, " + name;
}

// After TS 6.0: Parameter 'name' implicitly has an 'any' type.
// Fix:
function greet(name: string): string {
  return "Hello, " + name;
}
Enter fullscreen mode Exit fullscreen mode

Migration step: If you're not ready for strict mode, explicitly opt out in your tsconfig.json:

{
  "compilerOptions": {
    "strict": false
  }
}
Enter fullscreen mode Exit fullscreen mode

But honestly? Don't do this. Use the upgrade as motivation to finally enable strict mode. The type safety improvements catch real bugs. I found two null-reference issues in a production codebase just by enabling it.

Breaking Change #2: The Types Array Defaults to Empty

This one is subtle but impactful. Previously, TypeScript would automatically crawl node_modules/@types and include every type package it found. In 6.0, the types array defaults to [], meaning you must explicitly list your global type packages.

// tsconfig.json  Before TS 6.0
{
  "compilerOptions": {
    // types was implicitly ["node", "jest", "react", ...]
  }
}

// After TS 6.0  you need to be explicit
{
  "compilerOptions": {
    "types": ["node", "jest", "@testing-library/jest-dom"]
  }
}
Enter fullscreen mode Exit fullscreen mode

For Next.js projects, you'll also want to include the Next.js types:

{
  "compilerOptions": {
    "types": ["node", "react", "react-dom", "next"]
  }
}
Enter fullscreen mode Exit fullscreen mode

The upside? Build times drop significantly. The compiler no longer wastes time parsing type packages you're not using. In a monorepo with 200+ packages in node_modules/@types, I measured a 35% reduction in type-checking time just from this change.

Breaking Change #3: Deprecated Legacy Features

TypeScript 6.0 deprecates a long list of features that will be removed entirely in 7.0. If you're using any of these, you have one major version to migrate:

  • target: "es5" — Modern browsers don't need ES5. Use "es2022" or later.

  • --moduleResolution node (and node10) — Use "nodenext" or "bundler"

  • module: "amd" | "umd" | "systemjs" — These module systems are dead. Use "esnext" or "nodenext"

  • --baseUrl — Use path aliases in your bundler instead

  • --esModuleInterop false — This should have always been true

  • outFile — Use a proper bundler

Here's a before-and-after for a typical Next.js tsconfig.json:

//  Before: using deprecated options
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "baseUrl": ".",
    "esModuleInterop": true,
    "strict": true
  }
}

//  After: TypeScript 6.0 compatible
{
  "compilerOptions": {
    "target": "es2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["node", "react", "react-dom"],
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

New Feature: Temporal API Types

The ECMAScript Temporal API is the replacement for the Date object, and it's been a long time coming. TypeScript 6.0 includes full, first-class type support for Temporal, which means you get autocomplete, type checking, and documentation for every Temporal class.

// The old way — Date is mutable, confusing, and error-prone
const date = new Date("2026-04-04");
date.setMonth(date.getMonth() + 1); // mutates in place

// The Temporal way — immutable, explicit, type-safe
const date = Temporal.PlainDate.from("2026-04-04");
const nextMonth = date.add({ months: 1 }); // returns new instance

console.log(date.toString());      // "2026-04-04" (unchanged)
console.log(nextMonth.toString()); // "2026-05-04"
Enter fullscreen mode Exit fullscreen mode

Temporal shines when dealing with time zones, which has been a nightmare in JavaScript since forever:

// Convert between time zones with full type safety
const meeting = Temporal.ZonedDateTime.from({
  timeZone: "America/New_York",
  year: 2026,
  month: 4,
  day: 10,
  hour: 14,
  minute: 0,
});

const inTokyo = meeting.withTimeZone("Asia/Tokyo");
console.log(inTokyo.toString());
// "2026-04-11T03:00:00+09:00[Asia/Tokyo]"

// Duration arithmetic that actually works
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
const endTime = meeting.add(duration);

// Type-safe comparison
if (Temporal.ZonedDateTime.compare(meeting, endTime) 

        {date.toLocaleString("en-US", {
          month: "long",
          day: "numeric",
          year: "numeric",
        })}

      {daysUntil > 0 && (
        In {daysUntil} days
      )}

  );
}
Enter fullscreen mode Exit fullscreen mode

New Feature: Subpath Imports

TypeScript 6.0 now supports Node.js-style subpath imports using the #/ prefix under nodenext and bundler module resolution. This gives you clean import paths without the baseUrl hack:

// package.json
{
  "imports": {
    "#/components/*": "./src/components/*",
    "#/utils/*": "./src/utils/*",
    "#/hooks/*": "./src/hooks/*"
  }
}
Enter fullscreen mode Exit fullscreen mode
// Now you can import like this:
import { Button } from "#/components/Button";
import { useAuth } from "#/hooks/useAuth";
import { formatCurrency } from "#/utils/format";

// Instead of:
import { Button } from "../../../components/Button";
Enter fullscreen mode Exit fullscreen mode

The advantage over paths in tsconfig.json is that subpath imports work at runtime in Node.js without any build step, making them ideal for React Server Components and server-side code.

New Feature: Map Upsert Methods

TypeScript 6.0 adds types for the Stage 4 Map upsert methods: getOrInsert and getOrInsertComputed. These eliminate a common pattern that's been annoying JavaScript developers for years:

// The old way — verbose and error-prone
const cache = new Map();

function addToGroup(key: string, value: number) {
  if (!cache.has(key)) {
    cache.set(key, []);
  }
  cache.get(key)!.push(value); // non-null assertion needed
}

// The TypeScript 6.0 way — clean and type-safe
function addToGroupV2(key: string, value: number) {
  const group = cache.getOrInsert(key, []);
  group.push(value); // no assertion needed, type is number[]
}

// For expensive default values, use computed version
const expensiveCache = new Map();

async function getCached(key: string) {
  return expensiveCache.getOrInsertComputed(
    key,
    (k) => fetchFromDatabase(k) // only called if key is missing
  );
}
Enter fullscreen mode Exit fullscreen mode

The stableTypeOrdering Flag

This is a forward-looking feature that most developers will ignore — but shouldn't. The new --stableTypeOrdering flag helps identify code that depends on the order in which TypeScript resolves union types. Why does this matter? Because TypeScript 7.0's Go compiler may resolve types in a different order.

// This code might behave differently in TS 7.0
type Result = string | number;

// If your code depends on string being "first" in the union,
// --stableTypeOrdering will warn you

// Enable it now to find problems early:
// tsconfig.json
{
  "compilerOptions": {
    "stableTypeOrdering": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Run your test suite with this flag enabled. If everything passes, you're already safe for 7.0. If tests fail, you've found code that needs fixing before the Go migration.

Performance Wins: 20-50% Faster Builds

The combination of the empty types default and narrower rootDir scoping delivers measurable build improvements. Here are real numbers from migrating a production monorepo:

# Before TS 6.0 (TypeScript 5.7)
$ time tsc --noEmit
real    0m42.3s

# After TS 6.0 with explicit types array
$ time tsc --noEmit
real    0m24.1s

# That's a 43% improvement just from the defaults change
Enter fullscreen mode Exit fullscreen mode

For performance-conscious teams, this alone justifies the migration effort. Faster type-checking means faster CI pipelines, faster IDE responses, and shorter feedback loops during development.

Step-by-Step Migration Checklist

Here's the exact process I used to migrate three Next.js apps. Follow these steps in order:

  • Update TypeScript: npm install typescript@6.0

  • Add explicit types array to every tsconfig.json in your project

  • Replace deprecated moduleResolution: "node" with "bundler" (for Next.js/Vite) or "nodenext" (for pure Node.js)

  • Update target from "es5" to "es2022" or later

  • Remove baseUrl and migrate to paths or subpath imports

  • Enable stableTypeOrdering and run your test suite

  • Run tsc --noEmit and fix any new strict-mode errors

  • Update CI to use TypeScript 6.0

For most Next.js projects, the migration takes about an hour. The biggest time sink is adding explicit type imports if you were relying on automatic @types crawling.

Should You Upgrade Now?

Yes. TypeScript 6.0 is stable, the performance improvements are real, and — most importantly — everything deprecated in 6.0 will be removed in 7.0. The Go-based compiler is coming mid-2026, and it won't have a compatibility mode for legacy options.

Migrate now while deprecation warnings are gentle. In six months, they become compile errors.

The Temporal API types are a nice bonus if you're starting new projects, and the subpath imports support finally gives us a standards-based alternative to the baseUrl hack. But the real value of this release is the signal: modernize your TypeScript config now, or face a much harder migration to 7.0.


This article was originally published on NextFuture. Follow us for more fullstack & AI engineering content.

Top comments (0)