TypeScript 6.0: The Bridge Release

TypeScript 6.0 has been out for a few months, and it's the last compiler written in TypeScript and JavaScript. Microsoft is rewriting the compiler in Go (project codename Corsa) for TypeScript 7, delivering roughly 10x speed improvements on large codebases. 6.0 is the bridge: all features here are forward-compatible with 7.

1. Native Temporal Date/Time Types

The built-in Date API is painful. Months are zero-indexed, time zone handling is manual, and most projects pull in a library. TypeScript 6 ships type support for the TC39 Temporal API, giving you a modern date/time API with zero dependencies.

const launch = Temporal.PlainDate.from({ year: 2026, month: 9, day: 1 });
const twoWeeksLater = launch.add({ weeks: 2 });
const aMonthEarlier = launch.subtract({ months: 1 });

Time zones become straightforward. You can format the same instant across multiple zones without manual offset juggling:

const now = Temporal.Now.zonedDateTimeISO("America/Los_Angeles");
const zones = ["America/Los_Angeles", "America/New_York", "Europe/London", "Asia/Tokyo"];
for (const zone of zones) {
  const local = now.withTimeZone(zone);
  console.log(zone, local.toLocaleString());
}

Duration math is built in:

const today = Temporal.Now.plainDateISO();
const target = Temporal.PlainDate.from("2026-09-01");
const diff = today.until(target, { largestUnit: "day" });
console.log(`${diff.days} days until launch`);

No date library needed. No off-by-one month bugs. This is the feature most developers will adopt first.

2. Map.getOrInsert() and getOrInsertComputed()

Working with Map often requires check-then-set boilerplate:

const cache = new Map();
if (!cache.has("a")) {
  cache.set("a", 1);
}
const value = cache.get("a")!; // non-null assertion

TypeScript 6 adds Map.getOrInsert(), which combines the check and insert into one call:

const cache = new Map();
const value = cache.getOrInsert("a", 1); // returns 1, inserts if missing
cache.getOrInsert("a", 99);              // key exists, ignored
console.log(cache.get("a")); // still 1

The companion getOrInsertComputed() takes a factory function that only runs when the key is missing:

const cache = new Map();
cache.getOrInsertComputed("a", () => {
  console.log("computing a");
  return 1;
}); // logs "computing a", inserts 1
cache.getOrInsertComputed("a", () => {
  console.log("this never runs");
  return 2;
}); // factory skipped

If the value is expensive (database call, heavy calculation), getOrInsertComputed() skips the work when the key exists. getOrInsert() evaluates its argument unconditionally, so use the computed version for costly values.

3. RegExp.escape() for Safe Dynamic Regex

Building a regex from user input is error-prone. The . in the input acts as a wildcard:

const userInput = "1.0";
const pattern = new RegExp(userInput, "g");
const text = "version 1.0 and build 1x0 are different";
console.log(text.match(pattern)); // matches BOTH "1.0" and "1x0"

TypeScript 6 adds RegExp.escape(), which escapes special characters so they're treated literally:

const userInput = "1.0";
const pattern = new RegExp(RegExp.escape(userInput), "g");
const text = "version 1.0 and build 1x0 are different";
console.log(text.match(pattern)); // matches only "1.0"

Always wrap user-supplied strings in RegExp.escape() when constructing a regex.

4. # Subpath Imports

Deep relative imports like ../../../utils/format are fragile. The # prefix in subpath imports cannot collide with npm packages (scoped packages start with @).

Enable them in tsconfig.json:

{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler"
  }
}

Or for Node-native:

{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext"
  }
}

Then declare mappings in package.json:

{
  "imports": {
    "#utils/*": "./src/utils/*.js",
    "#components/*": "./src/components/*.js"
  }
}

Now imports are clean:

import { formatDate } from "#utils/format";
import { Button } from "#components/Button";

Without the correct moduleResolution, # imports won't resolve. This is the standardized version of path aliases.

5. Smarter Generic Inference

Generic types often required explicit annotations:

interface CreatePair {
  config: { value: T };
  consume: (value: T) => void;
}

const pair: CreatePair = {
  config: { value: 42 },
  consume: (value) => console.log(value),
};

TypeScript 6 infers T from the provided value:

const pair = {
  config: { value: 42 },        // T inferred as number
  consume: (value: number) => console.log(value),
};

Less boilerplate, full type safety.

tsconfig.json Changes to Make Now

  • strict now defaults to true. If you've set strict: false, reconsider.
  • verbatimModuleSyntax: true replaces importsNotUsedAsValues.
  • baseUrl is no longer required for path aliases.
  • moduleResolution: "node" is deprecated. Move to nodenext or bundler.

What's Coming in TypeScript 7

TypeScript 7 rewrites the compiler in Go. The Go compiler parallelizes across CPU cores, producing roughly 10x speed improvements on large codebases. All features in 6.0 are forward-compatible.

Next Steps

Adopt these APIs today. Audit your tsconfig.json to match 6.0 defaults. When TypeScript 7 arrives, the upgrade will be seamless.