TypeScript 6.0 arrived this week with a set of changes that, taken together, mark the clearest statement the language has made about its own direction in years. The headline is a concept the team calls erasable syntax, and understanding it requires understanding why Node.js changed what it does with .ts files.
What Erasable Syntax Means
Some TypeScript syntax is purely additive: type annotations, interfaces, type aliases, as casts. Strip them out and the JavaScript underneath runs exactly the same way it would have. Other TypeScript syntax is generative: enums, namespaces with values, parameter properties, legacy decorators. Remove those and you change what runs, because they emit JavaScript that would otherwise not exist.
TypeScript 6.0 makes this distinction formal. A new compilation mode restricts code to the erasable subset, meaning only syntax that can be removed by a simple text transformation with no code generation or runtime emission.
This matters because Node.js has been shipping experimental type stripping since Node 22.6, progressively stabilizing it through versions 22 and 23. The feature lets Node run .ts files directly by stripping type annotations before execution, without a full TypeScript compiler pass. It is fast, but it only works when the TypeScript you wrote does not depend on anything being emitted. An enum is not a type annotation; it is a runtime object that needs to be constructed, and Node cannot strip it.
TypeScript 6.0’s erasable mode is the compiler-side counterpart to Node’s stripper. Write erasable syntax and your .ts files become viable for direct execution in environments that support stripping. Use generative syntax and you stay in traditional compilation territory.
The Enum Situation
Enums are the most visible friction point in this new framing. A numeric TypeScript enum:
enum Direction {
Up,
Down,
Left,
Right
}
emits a runtime object with both forward (Direction.Up === 0) and reverse (Direction[0] === "Up") mappings. String enums are slightly simpler but still emit a runtime object. Neither is strippable.
TypeScript has not removed enums in 6.0, and existing code that uses them continues to compile in standard mode. The erasable mode refuses them, and the migration path is well-established: replace runtime enums with as const object literals.
const Direction = {
Up: "up",
Down: "down",
Left: "left",
Right: "right",
} as const;
type Direction = typeof Direction[keyof typeof Direction];
This pattern produces no runtime overhead beyond the object itself, participates in type narrowing, and works under any stripping-based toolchain. It has been idiomatic in strict TypeScript codebases for several years. TypeScript 6.0 does not mandate the migration, but the direction the ecosystem is heading is unmistakable.
Parameter properties in constructors follow the same logic. constructor(private name: string) emits this.name = name as part of the constructor body, so erasable mode disallows it. The explicit form is more verbose but transparent:
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
The verbosity is the point. Erasable syntax is visible syntax. Nothing is conjured at compile time.
Isolated Declarations Stabilized
The other significant 6.0 change is the stabilization of --isolatedDeclarations, introduced experimentally in TypeScript 5.5. The flag requires every exported symbol to carry an explicit type annotation sufficient to generate a .d.ts declaration file without running the full type checker.
Without this flag, TypeScript infers return types and complex types across the whole program. That inference is correct but expensive: generating declarations requires resolving the entire type graph. With --isolatedDeclarations, declaration emit becomes a file-by-file operation with no cross-file type resolution, which means build tools can parallelize it.
The practical impact on large monorepos is significant. Tools like Rollup, esbuild, and Vite can now generate .d.ts files in parallel across packages without waiting for the TypeScript compiler to finish a full check. Projects bottlenecked on declaration emit in their tsc --build pipelines will see faster incremental builds.
The trade-off is annotation verbosity on public surfaces:
// Errors under --isolatedDeclarations
export function parseConfig(raw: string) {
return JSON.parse(raw) as Config;
}
// Required form
export function parseConfig(raw: string): Config {
return JSON.parse(raw) as Config;
}
For well-maintained codebases this is a minor burden. For legacy code with deeply inferred public APIs it can require significant annotation work upfront. The TypeScript team treats this as a power-user flag rather than a default, which reflects the annotation burden it places on existing codebases.
Module Resolution and the ESM Cleanup
TypeScript 6.0 also tightens behavior around require() of ES modules. Under --moduleResolution node16 and nodenext, TypeScript now flags attempts to require() a module declared as ESM, either via a .mts extension or a "type": "module" field in its package.json. Node itself throws at runtime in this situation; TypeScript 6.0 catches it statically.
Earlier TypeScript versions were lenient about this in certain edge cases. The 6.0 behavior brings static analysis into alignment with what Node has long specified, closing a gap that has caused subtle interop bugs in mixed ESM and CJS codebases.
ES3 and ES5 emit targets are deprecated in 6.0. ES3 is effectively gone for new projects; ES5 now generates a deprecation warning. The practical minimum target going forward is ES2015. ES5 transpilation is largely the domain of dedicated tools like Babel or the output pipeline of a bundler anyway, so trimming this from TypeScript’s scope is a reasonable boundary to draw.
What This Means in Practice
For a project running TypeScript 5.x with no strict flags and no monorepo build pipeline, TypeScript 6.0 is a low-friction upgrade. Existing code compiles, enums work, and the breaking changes are narrow enough that most codebases will clear tsc --noEmit after a version bump with minimal fixes.
For libraries and monorepos with heavy build tooling, --isolatedDeclarations is worth evaluating now that it is stable. The annotation requirement feels restrictive at first, but the parallel declaration emit it enables pays back in build times as the codebase grows.
For code shipping to Node 22+ or Deno, or for anyone who wants .ts files to run directly without a compilation step, the erasable syntax constraints are worth adopting now. Migrate enums to as const objects, replace parameter properties with explicit field declarations, and the codebase becomes strippable.
The broader story TypeScript 6.0 is telling is about the relationship between the language and its host environments. The Node.js native type-stripping initiative and the TypeScript erasable mode are two sides of the same project: making TypeScript viable as a run-anywhere format rather than a compile-always source language. That shift has been building for years, through Deno’s early TypeScript support, through Bun’s built-in .ts execution, and through Node’s experimental stripper. TypeScript 6.0 is the compiler formally acknowledging that trajectory and giving it a proper home in the language specification.