Node.js 23: Experimental TypeScript Stripping
Node.js 23 dropped on October 16, and buried in the release notes between the V8 update and the require() changes is the feature TypeScript developers have been asking about for years: native TypeScript execution. You can now run node --experimental-strip-types app.ts and it works.
Mostly.
The implementation is more interesting than the headlines suggest, and the limitations matter more than the blog announcements let on. Let me walk through what’s actually happening under the hood and why it matters (or doesn’t) for your projects.
How Type Stripping Works #
The approach Node.js took is deliberately minimal, and that’s the smartest thing about it.
When you pass --experimental-strip-types, Node.js doesn’t compile your TypeScript. It doesn’t run tsc. It doesn’t generate JavaScript and then execute the output. Instead, it takes your TypeScript source and replaces type annotations with whitespace. That’s it. Types disappear; everything else stays exactly where it was.
This means your original source positions are preserved. Line 47, column 12 in your TypeScript file is still line 47, column 12 in the executed code. No source maps needed. Stack traces point to the right place in your .ts files. Debugging works without any mapping layer.
The engineering tradeoff is elegant: by replacing types with spaces instead of removing them (which would shift all subsequent characters), Node.js avoids the entire source map problem. No generation, no loading, no resolution of mapped positions at runtime. The performance cost of stripping is minimal compared to a full transpilation step.
Here’s a concrete example. This TypeScript:
function greet(name: string): string {
return `Hello, ${name}`;
}Becomes roughly:
function greet(name ) {
return `Hello, ${name}`;
}The colons, type annotations, and return type declarations are replaced with spaces. The function body is untouched. The character positions don’t shift.
What It Doesn’t Do #
This is where the “Node.js now supports TypeScript!” headlines mislead.
No runtime type checking. The types are erased before execution. If your function expects a string and receives a number, Node.js won’t stop it. This is consistent with TypeScript’s own philosophy (types are erased at compile time), but it means you’re still responsible for running tsc --noEmit or your linter for type safety.
No TypeScript-only syntax. Enums, namespaces, and const enum declarations aren’t plain type annotations — they require code generation. A const enum Direction { Up, Down } needs to become actual JavaScript values, not whitespace. The basic --experimental-strip-types flag can’t handle these.
There’s a separate --experimental-transform-types flag for that. This one does actual code transformation (not just stripping), which means it generates JavaScript and needs the heavier machinery. If your codebase uses enums extensively, you’ll need this flag — and you’ll pay the performance cost of real transpilation.
No tsconfig.json resolution. Node.js doesn’t read your TypeScript configuration file. Path aliases, baseUrl, rootDir — none of these are respected. If your project relies on path mapping (and many do), the native runner won’t resolve your imports correctly.
The Practical Impact #
For simple scripts and utilities, this is genuinely useful. I’ve already started using it for one-off scripts at work — the kind of thing where you’d previously run ts-node or tsx and wait for the compilation overhead. node --experimental-strip-types script.ts is faster because there’s no compilation step; it’s just string replacement.
For production applications? Not yet — and Node.js is explicit about this. The feature is experimental, which means the API could change or break between minor releases. Using it in CI/CD pipelines or production servers would be premature.
The sweet spot right now:
- Development scripts and utilities
- Quick prototyping without a build step
- CLI tools where startup time matters
- Testing individual files during development
For anything shipped to production, you still want a proper build step with tsc or a bundler like esbuild. The type stripping gives you convenience during development; it doesn’t replace your build pipeline.
Context: Node.js 22 LTS #
Node.js 22 was simultaneously promoted to LTS (Long Term Support) status the same week Node.js 23 launched. This matters because it means the stable, recommended version for production use doesn’t have type stripping at all. If you’re on LTS (and you should be for production workloads), this feature isn’t available to you.
Node.js 22 did get its own interesting addition — require() of synchronous ESM modules without a flag — which solves a different long-standing pain point. But the TypeScript support is a Node.js 23 (Current release line) feature only.
The Broader Trend #
Node.js adding TypeScript support (even experimentally) reflects a shift that’s been building for years. Deno shipped with TypeScript support from day one. Bun handles TypeScript natively. The tsx runner has been a popular community solution. Node.js was the holdout, and now it’s catching up.
The whitespace-replacement approach is Node.js being characteristically conservative. Rather than embedding a full TypeScript compiler (like Deno) or doing fast transpilation (like Bun with its native transpiler), Node.js chose the simplest possible implementation that could work. Strip types, preserve positions, avoid the source map problem entirely.
I respect the engineering decision. It’s the kind of pragmatic tradeoff that has kept Node.js stable for over a decade — move slowly, don’t break things, let the community prove out patterns before committing to them. The limitation is real (no enums, no path aliases), but the benefit (zero-overhead TypeScript execution for common cases) is equally real.
Will I use it in production? No. Not until it’s unflagged and stable — probably Node.js 24 or 25 at the earliest. Will I use it every day for scripts and development? Already am.
That’s the right place for an experimental feature to land: useful enough to adopt in low-stakes contexts, conservative enough to avoid breaking the ecosystem. Node.js 23 got this one right.