· 5 min read ·

TypeScript 6.0 and the End of TypeScript as a Transpiler

Source: typescript

Microsoft announced TypeScript 6.0 this week, and the major version bump is worth taking seriously. TypeScript has been incrementally releasing 5.x updates for several years, so hitting 6.0 signals something more than the usual quarterly feature drop. The changes in this release reflect a broader shift in how the TypeScript team thinks about what TypeScript should be: less a transpiler with a type system bolted on, and more a type layer that can be stripped away at runtime without any transformation.

This matters because of where the runtime ecosystem has been heading.

The Type-Stripping Moment

Node.js 22 shipped experimental support for running TypeScript files directly via --experimental-strip-types. The mechanism is deliberate: it strips type annotations using Amaro, a thin wrapper around oxc-transform, without running a full TypeScript compilation. No tsc, no declaration files, no tsconfig.json required. You write TypeScript, Node runs it.

Deno and Bun have supported this for years. But Node doing it changes the calculus entirely, because Node is where the majority of production TypeScript code runs. The question that framing immediately raises is: which TypeScript syntax actually works with type stripping, and which requires real compilation?

The answer is: most of it works, but not all of it. Enums, namespaces, and parameter properties do not just have type annotations removed when erased. They generate runtime JavaScript that has no equivalent in the original TypeScript syntax. A const enum member expands to its numeric value at use sites. A namespace compiles to an IIFE. A constructor parameter property like constructor(private name: string) compiles to an assignment inside the constructor body. These features cannot be handled by a simple strip pass.

erasableSyntaxOnly

TypeScript 5.8 introduced the --erasableSyntaxOnly compiler option specifically to address this. When enabled, the compiler errors on any syntax that cannot be erased without transformation. The practical effect is that enums, legacy namespaces, and parameter properties all become compile errors.

// With erasableSyntaxOnly: true
enum Direction {    // Error: Enums are not erasable
  Up,
  Down
}

const enum Status {  // Error: Const enums are not erasable
  Active = 1
}

class User {
  constructor(private name: string) {} // Error: Parameter properties are not erasable
}

The correct patterns under erasableSyntaxOnly look like this:

// Plain object as enum replacement
const Direction = {
  Up: 'Up',
  Down: 'Down'
} as const;
type Direction = typeof Direction[keyof typeof Direction];

// Explicit property + assignment
class User {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
}

These patterns are not new. The TypeScript community has been steering toward them for years precisely because const objects compose better with the type system than enums do, and explicit property declarations are more readable under scrutiny. erasableSyntaxOnly just makes the recommendation a hard error.

TypeScript 6.0 deepens this. The option is not new to 6.0, but the major release codifies the direction: TypeScript is moving toward being a pure annotation layer, and the features that required real emission work are legacy.

What Warranted a Major Version

TypeScript has historically been conservative about breaking changes, shipping them in minor versions with compiler flags and deprecation periods. A major version means the team felt some breaking changes needed to happen without the usual opt-in ceremony.

The breaking changes in 6.0 include dropped support for running the TypeScript compiler on Node.js versions below 18. This is not surprising given that Node.js 16 reached end-of-life in September 2023, but it does mean build pipelines still running on older Node versions need attention before upgrading.

Module resolution is another area. The TypeScript team has been rationalizing module resolution modes for several releases. The --module node16 and --moduleResolution node16 options, which were introduced alongside ESM support in TypeScript 4.7, have been gradually superseded by cleaner alternatives. TypeScript 6.0 takes steps to discourage or remove options that caused more confusion than they resolved, pushing users toward bundler resolution for projects using a bundler, and nodenext for projects running directly under Node.

There are also stricter type-checking behaviors that were behind flags in 5.x and are now on by default, or that have been refined in ways that surface errors in code that previously passed. Codebases with strict: true should be largely unaffected, since most of these refinements were already covered under the strict suite. But projects that opted into individual strict checks piecemeal may see new errors.

Enums Are Not Going Away, But They Are Being Sidelined

It is worth being direct about this: TypeScript 6.0 does not remove enums. They still work. The erasableSyntaxOnly flag is opt-in. But the trajectory is clear. The TypeScript team’s FAQ on enums has long carried a note that object literals with as const are often preferable. The erasableSyntaxOnly flag exists specifically because enums are incompatible with the native type-stripping model that Node.js, Deno, and Bun have converged on.

The irony is that enums were one of TypeScript’s most distinctive early features. When TypeScript launched in 2012, enums were a differentiator from plain JavaScript. They felt like a real language feature rather than just annotated JavaScript. But that compile-time code generation is exactly what makes them incompatible with a world where TypeScript is treated as syntax sugar over JavaScript rather than a separate language.

For new projects, the guidance is straightforward: use object literals with as const and derive the union type from them. For existing projects with heavy enum usage, migration is mechanical but potentially tedious depending on codebase size.

The Broader Implication

TypeScript 6.0 represents a bet on a particular model of what TypeScript is. The bet is that TypeScript’s long-term value is in the type system and editor tooling, not in being a compilation target. That the right relationship between TypeScript and JavaScript is one where TypeScript adds type annotations that tools can strip, rather than one where TypeScript generates different JavaScript than you wrote.

This model has real benefits. Type-only imports become cleaner. The gap between what you write and what runs narrows. Debugging becomes less about source maps and more about reading the actual code. Native TypeScript execution in Node means one fewer build step for many workflows.

The cost is that some of TypeScript’s early design decisions, the ones that required emission, become technical debt. Enums, namespaces, the old decorator implementation, parameter properties. These are features that made TypeScript feel like a real language in 2014 but are awkward in 2026.

The 5.x series took TypeScript’s type system to a remarkable level of expressiveness: inferred type predicates, variadic tuple types, template literal types, improved inference for complex generics. TypeScript 6.0 is less about new type system capabilities and more about cleaning up the runtime story, aligning the language with the direction the ecosystem has already chosen.

For most TypeScript users, upgrading from 5.x to 6.0 will be a matter of updating Node, running the compiler, and addressing a handful of new errors. The bigger shift is conceptual, and that shift has been happening incrementally for a few years now. 6.0 just puts a version number on it.

Was this interesting?