TypeScript’s version numbering has never mapped cleanly to semantic versioning. A TypeScript major version isn’t a guaranteed signal of sweeping breaking changes; the language has a long history of introducing stricter behaviors behind opt-in flags and maintaining backward compatibility across major bumps. The jump from 4.x to 5.0 in March 2023 was more a calendar landmark than a hard break. TypeScript 6.0, announced in March 2026, arrives in a meaningfully different ecosystem than any previous release, because TypeScript no longer has a monopoly on running TypeScript code.
The Type-Stripping Runtime Landscape
When TypeScript 1.0 shipped in 2014, tsc was the only path from .ts source to executable JavaScript. That changed gradually. Deno launched in 2018 with built-in TypeScript support via transpilation. Bun followed. Then, in 2024, Node.js 22.6 shipped --experimental-strip-types, using a type-stripping parser backed by SWC to remove type annotations at load time without compiling them. By Node.js 23, the feature was enabled by default. By early 2026, three of the most widely-used JavaScript runtimes can execute .ts files directly.
The implementation model matters here. Type stripping is not compilation. It removes type annotations from the source text and executes the remaining JavaScript. It cannot handle TypeScript syntax that emits JavaScript code, because there is no emit step. Concretely, that means enums, namespaces, parameter properties, and legacy experimental decorators all break the type-stripping model. An enum like:
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE",
}
compiles to the following JavaScript:
var Status;
(function (Status) {
Status["Active"] = "ACTIVE";
Status["Inactive"] = "INACTIVE";
})(Status || (Status = {}));
There is no way to erase an enum. It requires code generation. The same is true for namespaces and for constructor parameter properties like constructor(public x: number), which expands into explicit property assignments inside the constructor body.
--erasableSyntaxOnly
TypeScript has been working toward a compiler flag that enforces compatibility with the type-stripping model. The flag prohibits TypeScript-specific constructs that require code generation rather than simple erasure, producing hard errors when you use syntax that would break under a type-stripping runtime.
With this flag enabled, the compiler rejects the enum example above, any use of namespace, and parameter properties in constructors. The compatible alternatives are straightforward:
// Instead of enum, use a const object with a union type:
const Status = {
Active: "ACTIVE",
Inactive: "INACTIVE",
} as const;
type Status = typeof Status[keyof typeof Status];
// Instead of a namespace, use module-level exports:
export function helper(x: string): string {
return x.trim();
}
// Instead of a parameter property, use explicit assignment:
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
None of these alternatives are worse code. The const-object-plus-union-type pattern is already preferred in many style guides because it composes better with TypeScript’s structural type system than enums do. Const enums aside, regular enums have always had a somewhat uncomfortable relationship with the rest of the type system: they are nominal in a language that is otherwise structural, and they generate runtime code in a way that surprises developers who expect TypeScript to disappear at runtime.
Explicit property declarations in class constructors are similarly more readable to developers coming from languages without parameter property shorthand. The flag enforces constraints that the type-stripping runtime ecosystem requires, and those constraints happen to align with patterns that are broadly more portable and easier to reason about.
isolatedDeclarations and Build Performance
TypeScript 5.5 introduced isolatedDeclarations, a flag that enforces that each file’s declaration (.d.ts) output can be generated without processing the rest of the project. This enables build tools to parallelize declaration emit rather than waiting for the full type-checking pass to complete.
The practical effect in large monorepos is meaningful. Tools like esbuild, Vite, and Turbopack can generate declarations in parallel across packages when isolatedDeclarations is enabled, instead of serializing through tsc --declaration. TypeScript 6.0 continues building on this foundation, with declaration emit becoming architecturally decoupled from type checking as a deliberate goal.
This decoupling matters beyond raw build speed. It means that the TypeScript language service, the type checker, and the declaration emitter can evolve more independently, and that tooling authors have a cleaner interface to work against when building custom pipelines. For monorepos at scale, the shift from sequential to parallel declaration emit can be the difference between a CI pipeline that is tolerable and one that is not.
Module Resolution
TypeScript’s module resolution has accumulated significant complexity over the years. The node16 and nodenext settings, introduced in TypeScript 4.7, enforce proper ESM semantics including the requirement to use .js extensions in relative imports even when the source file is .ts. This has been a persistent source of friction because it feels counterintuitive to write:
import { helper } from "./utils.js"; // source file is utils.ts
The reason is that TypeScript, under nodenext, emits JavaScript that must work in a native ESM runtime. The .js extension in the import refers to the compiled output path. TypeScript rewrites types, not import specifiers. This is consistent with how ESM module resolution actually works at the runtime level, but it requires a mental model adjustment for developers accustomed to the CommonJS era where bundlers and build tools handled all path resolution.
TypeScript 6.0 continues tightening the module resolution story to reflect where the ecosystem has landed. With Node.js 23’s native TypeScript support and the broad adoption of Vite and other ESM-first toolchains, the nodenext and bundler module resolution modes have moved from advanced options to mainstream choices. The 6.0 release makes the module system configuration more coherent for projects already living in this space, while continuing to deprecate legacy behaviors that made sense in 2016 but are actively confusing in 2026.
Migration Path
For most existing TypeScript projects, the upgrade to 6.0 should be incremental:
- Update
typescriptinpackage.jsonand runtscwith your existing configuration. - Address any errors from removed or changed behaviors, which are documented explicitly in the TypeScript release notes.
- If your project targets Node.js 23+ or another type-stripping runtime, add
--erasableSyntaxOnlyto yourtsconfig.jsonand migrate enum and namespace patterns. - In monorepos, evaluate
isolatedDeclarationsper package to unlock parallel declaration emit in supported build tools.
The heaviest migration work under --erasableSyntaxOnly comes from enum usage. The conversion from enum to const-object-plus-union-type is mechanical enough that a codemod handles most cases. Projects that use enums at public API boundaries, particularly in published packages, will need to think carefully about whether to migrate the public surface or maintain enum support for existing consumers.
Namespaces are less common in modern TypeScript, but they persist in older codebases, particularly those that grew out of the DefinitelyTyped era when namespaces were the primary way to organize ambient type declarations. Those patterns are worth cleaning up regardless of 6.0.
TypeScript Toward the TC39 Future
The TC39 type annotations proposal is advancing through the standards process. If it reaches Stage 4 and gets implemented in JavaScript engines, TypeScript-style type syntax would become natively ignorable by runtimes without any preprocessing at all. That is still a long road, but the direction has been set for several years.
TypeScript 6.0 is a release that prepares the language for that future without waiting for it. The --erasableSyntaxOnly flag, the native runtime integrations, the continued investment in isolatedDeclarations, and the ESM-first module story are all consistent with one direction: TypeScript as annotations layered on JavaScript, rather than a separate language that compiles down to it.
That distinction is subtle but has accumulated significance over a decade of TypeScript development. The language has always been technically a superset of JavaScript, but the workflow has involved a build step that made it feel like a distinct tool in the chain. TypeScript 6.0 is the release at which that build step becomes genuinely optional for a meaningful portion of TypeScript projects, and the language’s design is increasingly shaped by that reality.