Node.js Type Stripping Killed TypeScript Enums — The Language Is Now Officially Bifurcated Into "Erasable" and "Runtime" Syntax

TypeScript developers have been debating this for months, and the dust is finally settling — but the landscape it reveals is more fractured than anyone expected. Node.js v25.2.0 stabilized type stripping, which means you can now run node app.ts directly. No compilation step. No tsconfig.json. No build pipeline for simple scripts. It sounds like the developer experience TypeScript always deserved, and in many ways it is. But there’s a catch that fundamentally changes how we think about the TypeScript language itself.

Node.js strips types at the textual level without understanding TypeScript’s semantics. It literally removes type annotations from the source code and runs what’s left as JavaScript. This means “erasable” syntax — type annotations, interfaces, type aliases, generics — works perfectly. The runtime just deletes them and the remaining JavaScript is valid. But “runtime” syntax — enums, namespaces, parameter properties, const enum — is banned. These features don’t just annotate JavaScript; they generate JavaScript code. An enum like enum Direction { Up, Down } compiles to an actual JavaScript object with reverse mappings. You can’t just erase it; you have to transform it. Node.js refuses to do that transformation, so it throws an error.

TypeScript 5.8 responded by adding the --erasableSyntaxOnly compiler flag. When enabled, tsc will error if your code uses any non-erasable features. This flag essentially formalizes the bifurcation: there is now “erasable TypeScript” (the subset that runs everywhere) and “full TypeScript” (the complete language that requires compilation).

Why This Matters More Than You Think

TypeScript enums have been controversial for years. They generate runtime JavaScript objects, add bundle size, and behave differently from every other TypeScript construct. Many style guides — including several from large tech companies — already recommend as const objects over enums. The TypeScript team itself has hinted that enums were a design decision they might not repeat.

But enums are deeply embedded in millions of codebases. Angular uses them extensively throughout its framework and documentation. NestJS patterns rely on them for decorators and guards. Countless enterprise applications have enums woven through their domain models, API contracts, and database mappings. You can’t just flip a compiler flag and call it a day.

The bifurcation creates a real practical problem: code that compiles and runs with tsc might fail with node --experimental-strip-types (which is graduating to stable). If you’re writing a library, do you target erasable-only TypeScript to maximize compatibility, or do you use the full language? If you’re starting a new project, do you adopt the simpler node app.ts workflow and give up enums, or do you keep your build pipeline for the full feature set?

The Developer Experience Tradeoff

For new projects, the simplification is genuinely fantastic. node app.ts is zero-config, instant execution. No tsc --watch, no ts-node, no tsx — just Node.js running your TypeScript directly. For scripts, CLIs, and small services, this is a massive quality-of-life improvement.

For existing projects with enums and namespaces, there’s a migration cost. I spent a weekend migrating a side project: 45 enums converted to as const objects with derived types. The resulting code was actually cleaner in many ways — TypeScript’s type inference with as const is remarkably powerful, and the patterns are more composable than enum types. But the migration wasn’t trivial. Enum comparison semantics differ from string literal comparisons in subtle ways. Several switch statements needed updating because exhaustiveness checking works differently. A few runtime checks that relied on Object.values(MyEnum) had to be rewritten.

Ecosystem Convergence

Here’s what makes this feel inevitable rather than optional: Deno has always stripped types without supporting runtime features. Bun currently supports enums but is signaling alignment with Node.js’s approach. The three major JavaScript runtimes are converging on erasable-only TypeScript as the standard.

This convergence is creating a de facto standard. The question is whether it fractures the community into “modern TypeScript” (erasable only, runs everywhere without compilation) and “full TypeScript” (with enums and namespaces, requires a build step). We’ve seen similar bifurcations before — CommonJS vs ESM took years to resolve and arguably still isn’t fully resolved in practice.

My prediction: within two years, new TypeScript projects will overwhelmingly use erasable-only syntax. Existing projects will migrate gradually, probably tied to major version bumps or framework upgrades. Enums won’t disappear from the language spec, but they’ll become a legacy feature — supported but not recommended.

Has your team adopted Node.js type stripping yet? Are you planning to remove enums from your codebase, or is the migration cost too high to justify?

This hits close to home. Our design system library has 23 enumsButtonVariant, SpacingScale, ColorToken, IconSize, BreakpointKey, and so on. They’re part of our public API surface, which means removing them isn’t a refactor — it’s a breaking change. We can’t just swap to as const without a major version bump and a migration guide for every consuming team across the organization.

This is the real cost of the bifurcation that doesn’t get discussed enough: library authors bear the migration burden for a decision made by runtime implementers. Our consumers didn’t ask for this change. They’re happily using ButtonVariant.Primary in their components and it works fine with their existing build pipelines. But if we want our library to be compatible with the growing number of teams experimenting with Node.js type stripping for their tooling and scripts, we need to support the erasable-only world.

Our plan — and I’m not thrilled about it — is to support both patterns during a transition period. We’ll continue exporting enums for backward compatibility and simultaneously export as const equivalents (BUTTON_VARIANT alongside ButtonVariant) for teams using type stripping or targeting modern TypeScript conventions. That’s double the API surface for at least a year, probably longer, until we can deprecate enums in the next major version.

The tooling story also matters for design systems. Our Storybook setup, our documentation generators, and our prop table extraction all understand enums. The as const pattern requires different tooling to extract the same developer-facing documentation. It’s not insurmountable, but it’s another hidden cost.

I agree that as const is technically superior for most use cases. But the migration path for public APIs is painful, and I wish the conversation around type stripping acknowledged that more honestly.

We evaluated Node.js type stripping for our backend services last quarter and made the decision to wait. The headline benefits are real — our local development build step dropped from 45 seconds to essentially instant when we prototyped with node --experimental-strip-types. That’s a meaningful DX improvement, especially for a team of 40 engineers doing dozens of restart cycles per day.

But here’s the spreadsheet reality: we’d need to refactor 200+ enums across 15 microservices. Our shared types package — consumed by every service — has 67 enums that define domain concepts like order states, payment methods, and feature flags. Updating those means coordinated changes across every consuming service, updated tests, updated API documentation, and retraining the team on as const patterns and the subtle differences in exhaustiveness checking.

We estimated the migration at roughly 6-8 engineering weeks spread across multiple teams. The ROI calculation doesn’t work when our current tsc build pipeline runs fine and our CI/CD handles it without issues. The 45-second build time is annoying but not a bottleneck — it’s a coffee break, not a productivity crisis.

My prediction for how this plays out across the industry: new projects will use erasable-only TypeScript by default within a year. It’s just simpler, and new projects don’t have migration debt. Existing projects will stick with full TypeScript until they hit a natural rewrite opportunity — a major framework upgrade, a service decomposition, or a team restructuring that creates a window for modernization.

The bifurcation will persist for 3-5 years, similar to the CommonJS/ESM transition. That transition is technically resolved — ESM won — but practically it’s still ongoing. Plenty of production Node.js code still uses require(). The enum-to-as const migration will follow the same pattern: technically clear, practically slow.

For engineering leaders making this decision: don’t let FOMO drive a premature migration. Evaluate the actual cost against the actual benefit for your specific codebase. Sometimes “boring and working” is the right engineering choice.

React Native developers are watching this bifurcation very closely, and I don’t think the web-centric TypeScript community fully appreciates the cross-platform implications.

Our architecture has a shared business logic layer that runs on both web (React) and mobile (React Native). This shared layer is where most of our enums live — order statuses, payment states, permission levels, notification types, feature flags. These enums are consumed by web components, React Native screens, and our Node.js backend. The as const pattern works fine for the web target, but React Native tooling handles enums differently.

The specific issue: we have native module bridges that map TypeScript enums to native enums on iOS (Swift) and Android (Kotlin). Our bridge generation tool parses TypeScript enum declarations and produces corresponding native enum types. Switching to as const objects breaks this tooling because the parser expects the enum keyword syntax. We’d need to update our bridge generator, which is a custom internal tool that nobody wants to touch.

There’s also the Metro bundler consideration. Metro already handles TypeScript, and it supports the full language including enums. If the broader Node.js ecosystem moves to erasable-only TypeScript and tooling starts assuming that convention, we could end up in a situation where web-targeting tools drop enum support while mobile-targeting tools still expect it. That’s a compatibility nightmare for shared codebases.

The cross-platform story gets genuinely complicated when different targets support different TypeScript feature subsets. Web targets via Node.js type stripping: erasable only. React Native via Metro: full TypeScript. Server via Deno: erasable only. Server via Bun: currently full, moving to erasable. You end up targeting the lowest common denominator — erasable only — even if some of your targets handle enums fine.

I’m cautiously optimistic that this resolves itself, but the transition period for cross-platform teams is going to be rougher than for pure web teams. We’re holding off on any enum migration until we see clearer signals from the React Native and Metro teams about their direction.