
TypeScript 6.0 marks an important shift in TypeScript history; it is the last major release built on the existing JavaScript codebase. Microsoft is rewriting the entire TypeScript compiler and language service in Go, and TypeScript 6.0 serves as the bridge that leads us to the upcoming TypeScript 7.0, which promises near 10x faster build times.
But beyond the architectural shift, what matters most for everyday developers are the changes focused on best practices. Instead of requiring developers to opt into best practices, TypeScript now assumes them. The era of boilerplate tsconfig setup is ending.
In this article, we'll explore the most impactful changes and how you can start using them in your projects today.
Updated tsconfig.json Defaults
- strict ⇒ now true by default
- module ⇒ now defaults to esnext
- target ⇒ now defaults to the latest ECMAScript version
- noUncheckedSideEffectImports ⇒ now defaults to true
- libReplacement ⇒ now defaults false
Collectively, these updates signal a clear direction: TypeScript 6.0 assumes modern JavaScript, strict typing, and up-to-date runtimes by default, rather than requiring developers to opt in.
New Features in TypeScript 6.0
- 1.New Types for Temporal: Like most devs, you are probably tired of wrestling with JavaScript's Date class timezone headaches, mutable objects, and an API that never quite does what you expect. Temporal proposal aims to fix all of that, and it's now at Stage 3 of the TC39 process, meaning it's all but certain to land in JavaScript. TypeScript 6.0 is already ahead of the curve, shipping types for the Temporal API so you can start working with them today.
// Current moment in time, like Date.now() but immutable const now = Temporal.Now.instant(); // Temporal.Instant // A calendar date with no time or timezone attached const date = Temporal.PlainDate.from('2026-03-04'); // Temporal.PlainDate - 2.Less Context-Sensitivity on Functions: To understand why this change matters, we first need to understand how TypeScript infers types in generics In Methods vs Functions. Consider this example:
type CallFunction = <T>(obj: { fn: (x: T) => void; value: T; }) => void; const callFunc: CallFunction = (obj) => { obj.fn(obj.value); }; // TypeScript infers T as string from the annotation callFunc({ fn: (name: string) => console.log(name.toUpperCase()), value: "TypeScript User" });Looking at this code, TypeScript has two simple jobs: infer the type of the argument passed as T, then use that type for x; because it knows arrow functions are stable.The problem occurs when the function is context-sensitive, meaning it uses method shorthand syntax with no explicit type annotation on its parameter. In that case, TypeScript can't accurately infer the type without risking circular reasoning: to type the function it needs T, but to know T it needs to type the function. To avoid going in circles, TypeScript skips context-sensitive functions and looks for other primitive clues first.
// Method shorthand triggers context-sensitivity callFunc({ /* TypeScript sees method shorthand syntax and TypeScript thinks: "This might use 'this', skip it!" TypeScript skips fn entirely and looks for T elsewhere */ fn(name) { console.log(name.toUpperCase()); // 'name' is of type 'unknown' in older TypeScript }, // T is eventually inferred from value instead value: "TypeScript User" });Previously, TypeScript skipped method shorthand functions entirely when inferring types. In TypeScript 6.0, the compiler is smarter, it first checks whether the method actually uses `this`. If it doesn't, TypeScript treats it just like an arrow function and infers the type immediately instead of skipping it.
// TypeScript 6.0 — method shorthand no longer skipped callFunc({ /*TypeScript checks: "Does fn use 'this'?" — No. So it treats it like an arrow function and infers T as string*/ fn(name) { console.log(name.toUpperCase()); // 'name' is now correctly inferred as string }, value: "TypeScript User" });The result: because `fn` doesn't use `this`, TypeScript no longer considers it context-sensitive. It infers `T` directly from `fn`'s parameter instead of skipping it and falling back to `value`.
- 3.RegExp.escape: Before TypeScript 6.0, if you wanted to use a string containing special regex characters like `.`, `*`, `+`, `?`, `^`, or `$` inside a Regular Expression, this usually dont work correctly, because these characters carry special meaning: like `.` means "any character" and `*` means "zero or more." If your string happened to contain any of them, your regex would silently break or behave unpredictably.
const search = "$10.00"; const regex = new RegExp(search); // The regex thinks "$" is the "end of line" anchor // and "." is "any character". It won't find "$10.00" correctly!Instead of manually adding backslashes (e.g. `\$10\.00`) to every special character, `RegExp.escape` does it for you automatically treating it as text and not as a pattern.
const search = "$10.00"; const safeSearch = RegExp.escape(search); // "\$10\.00" const regex = new RegExp(safeSearch); console.log(regex.test("$10.00")); // true - 4.The dom lib Now Contains dom.iterable and dom.asynciterable: Before TypeScript 6.0, the Web API types (the "DOM") were split into three separate pieces in TypeScript: `dom`, `dom.iterable`, and `dom.asynciterable`. Because many older browsers didn't support iteration, TypeScript forced you to explicitly add `dom.iterable` to your config if you wanted to loop over DOM elements. Your `tsconfig.json`
{ "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"] } }Since almost all modern web development environments support iteration, there's no reason to keep them separate. In TypeScript 6.0, `dom.iterable` and `dom.asynciterable` have been merged directly into the main `dom` library. You can now just write `"dom"` and get everything automatically:
{ "compilerOptions": { "lib": ["dom", "esnext"] } } // For...of on NodeList works without any extra config for (const el of document.querySelectorAll('div')) { console.log(el.textContent); } // for await...of on ReadableStream also works out of the box async function readStream(stream: ReadableStream<string>) { for await (const chunk of stream) { console.log(chunk); } }
Breaking Changes & Default Adjustments
| Feature | Old Behavior | New Behavior in 6.0 |
|---|---|---|
| rootDir Default | Varied based on file structure. | Now defaults to . (the current directory). |
| types Default | Included all @types packages found. | Now defaults to [] (empty) to improve performance and prevent global "pollution". |
| Command-Line Files | You could list files AND use a tsconfig. | This is now an Error. You must use one or the other. |
| DOM Libraries | dom.iterable and dom.asynciterable were separate. | They are now fully included in the main dom library. |
Deprecations in 6.0
| Feature | Replacement | Reason for Removal |
|---|---|---|
| target: es5 | Upgrade to es2015 or later. | ES5 is extremely old; most tools handle downleveling better than TS. |
| --downlevelIteration | Use a modern target or external transpiler. | Part of the ES5 deprecation path. |
| module: amd, umd, systemjs | Use esnext or commonjs. | These formats are largely obsolete for modern development. |
| moduleResolution: node | Use node16, nodenext, or bundler. | "Node10" (legacy) resolution is often inaccurate for modern packages. |
| --baseUrl | Use paths or Node's subpath imports. | Causes confusion with relative vs. absolute imports. |
| --outFile | Use an external bundler (esbuild, Vite, etc.). | Complex to maintain; external tools are much faster and better at this. |
| module Foo { ... } | Change to namespace Foo { ... }. | Avoids conflict with the upcoming "ECMAScript Modules" proposal. |
| asserts { type: "json" } | Change to with { type: "json" }. | The JS spec changed from asserts to with (Import Attributes). |
| --alwaysStrict: false | Always use strict mode. | Modern JavaScript and modules are strict by default. |
Migrating to TypeScript 6.0
Upgrading to TypeScript 6.0 is straightforward, simply install the Release Candidate (RC) or the latest stable 6.0 version, use:
npm install -D typescript@rc # OR specifically npm install -D typescript@6.0.1-rc
Now Review the following checklist to clean up your config and prepare for TypeScript 7.0:
- Clean Up Tsconfig: Since TypeScript 6.0 is a "bridge" to the native Go-based version 7.0, you should follow these manual steps to fix the most common deprecation warnings:
- Update
tsconfig.json lib: You no longer need to split the DOM iterables switch to Es2025 that gives you access toRegExp.escapeandTemporal{ "compilerOptions": { "lib": ["dom", "es2025", "esnext"] } } - Fix Import Attributes: If you imported JSON or other assets, using the
assertskeyword is now an error. Replace it withwithsee example below// Before 6.0 import data from "./file.json" asserts { type: "json" }; // In TypeScript 6.0 and later import data from "./file.json" with { type: "json" }; - Replace Namespaces: If you still use
moduledeclarations, switch them tonamespace. you should use a find and replace feature on your IDE to update them// Before 6.0 module MyNamespace { ... }; // In TypeScript 6.0 and later namespace MyNamespace { ... };
- 7.0 prep: I like think of TypeScript 6.0 as compatibility check although there are not a tone of new features, bu by upgrading to 6.0 and fixing the deprecation warnings, you are ensuring your project is ready to reap the massive performance benefits of the native Go-based compiler the moment TypeScript 7.0 launches.