TypeScript 5.8 quietly bets on type stripping
--erasableSyntaxOnly exists because Node can now run TypeScript, and enums can't be erased.
TypeScript 5.8 shipped with a flag that looks like a footnote and is actually a statement about where the language is going: --erasableSyntaxOnly.
the context#
Node.js can now strip TypeScript types and run the resulting JavaScript directly. Deno and Bun have done this for a while. The approach is deliberately dumb — it does not typecheck, it does not transform, it replaces type annotations with whitespace and runs what is left.
That works beautifully for the ninety-five percent of TypeScript that is JavaScript plus annotations. It breaks completely for the parts of TypeScript that generate code:
enum Color { Red, Green } // emits an object at runtime
namespace Utils { export const x = 1 } // emits an IIFE
class Point {
constructor(private x: number) {} // parameter properties emit assignments
}
declare module "foo" {} // fine, erasableYou cannot erase an enum. There is nothing to erase to; the enum is runtime code wearing type-shaped syntax.
what the flag does#
erasableSyntaxOnly: true makes the compiler reject any construct that cannot be removed by whitespace substitution. Enums, namespaces with runtime values, parameter properties, and old-style import = are all errors.
{
"compilerOptions": {
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"module": "nodenext"
}
}Turn it on and your TypeScript is guaranteed to run under any type-stripping runtime with no build step at all.
the alternatives to what it bans#
You are losing very little.
Instead of an enum, a const object with a derived union type — which most codebases already prefer because it produces cleaner types and does not have enum's bizarre bidirectional-mapping behavior:
const Color = { Red: "red", Green: "green" } as const;
type Color = typeof Color[keyof typeof Color]; // "red" | "green"Instead of parameter properties, an explicit assignment. Two extra lines, and the ES class fields proposal made the ergonomics fine anyway.
Instead of namespaces, modules. You should have done this in 2019.
const enum remains available with preserveConstEnums, but the honest advice is to stop using it — it has never played well with isolated module compilation and it never will.
also in 5.8#
- Checked returns for conditional expressions. Return statements whose type is a conditional now get checked against each branch individually rather than against the collapsed union, which catches a real class of bug.
--module nodenextsupportsrequire()of ESM, tracking Node's own change.- Faster program updates on the editor path, which you will feel in a large monorepo more than any feature.
the direction#
The build step for TypeScript is dissolving. Between type stripping in runtimes, tsc --noEmit as a pure typechecker in CI, and bundlers that handle TypeScript natively, the days of TypeScript-as-a-compile-target are ending.
What remains is TypeScript as a type system layered on JavaScript — which is what it always claimed to be and only recently became true. Set the flag, delete your enums, and stop thinking about the build.
— Dom, March 1, 2025