
Node 26 Runs TypeScript. The Escape Hatch Is Gone.
Quick answer: Node runs .ts files directly by stripping types, with no build step and no transform. Node 26 removed --experimental-transform-types, the flag that used to compile the syntax stripping cannot handle. Enums, namespaces with runtime code, parameter properties and decorators are now permanently unsupported in Node itself, and tsconfig.json is ignored entirely. Whether you can drop your build step depends on whether your code uses any of that.
Running TypeScript without a bundler stopped being a party trick some time ago. Type stripping has been on by default since Node 22.18.0 and 23.6.0, and the official documentation marks it stable as of 24.12.0 and 25.2.0. The interesting change in Node 26 is not an addition. It is a removal.
Node 26 deleted --experimental-transform-types. That flag was the bridge for teams whose code used TypeScript features that cannot be erased. With it gone, the line between “Node can run this” and “you still need a compiler” is now fixed, and it runs straight through a lot of existing code.
Type stripping is erasure, not compilation
The mental model is one sentence: Node removes the type annotations and replaces them with whitespace, then runs the result as JavaScript. Nothing is transformed. Nothing is downlevelled. Line numbers stay intact, which is why stack traces line up without source maps.
That design is why it is fast and why it is limited. Anything that exists only as a type disappears cleanly. Anything that has to emit runtime JavaScript — a construct where the TypeScript compiler generates code that was never in your source — has nowhere to go.
What Node refuses to run
Four constructs throw ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, and one fails earlier, at the parser:
| Construct | Why it fails | What to use instead |
|---|---|---|
enum Status { ... } |
Emits a runtime object | A const object with as const, plus a derived union type |
| Namespace with runtime code | Emits an IIFE and assignments | ES modules; type-only namespaces still work |
Parameter properties (constructor(private x: T)) |
Emits assignments in the constructor | Declare the field and assign it explicitly |
Import aliases (import A = B.C) |
Emits a runtime binding | Standard ESM import syntax |
| Decorators | Not yet a JavaScript feature; parser error | A compiler, or restructure to avoid them |
Type-only namespaces are fine, because they vanish. A namespace that exports a value does not. Decorators are the sharpest edge of the five: they are not a Node limitation but a language one, and they are load-bearing in large parts of the Node ecosystem. If your service is built on a decorator-driven framework, this whole conversation ends here and you keep your build step.
Why removing the transform flag matters
Until Node 26, a team hitting one of those constructs had an option: turn on --experimental-transform-types and let Node compile rather than erase. It was experimental, it was slower, and it broke the one-to-one line mapping, but it existed. It does not exist any more.
The signal is clear enough. Node is not going to become a TypeScript compiler. It will erase types and nothing else, and everything beyond that belongs to tsc, a bundler, or a runtime that made the opposite choice. That is a defensible line — a runtime that quietly compiles your source is a runtime that owns your semantics — but it means “we will migrate later, with the flag” is no longer a plan.
The import rules break codebases before the syntax rules do
In practice most teams do not trip on enums. They trip on module resolution, because Node applies ESM rules with no compiler-style convenience layer.
Extensions are mandatory and must be the real ones
import './util.ts' works. import './util' does not, and neither does the .js extension that TypeScript users were trained to write for NodeNext output. You are importing the file that exists, not the file a compiler would have produced.
tsconfig.json is ignored
Node does not read it. Not for paths, not for baseUrl, not for target. Every path alias in your codebase resolves to nothing. The documented replacement is subpath imports — the imports field in package.json, with specifiers starting # — which is a real standard but not a drop-in rename of your aliases.
node_modules is off limits, and .tsx is not supported
Node refuses to strip types from files inside node_modules, deliberately, to discourage publishing TypeScript source to the registry. And .tsx is unsupported outright, which rules the feature out for most front-end work regardless of everything above.
A decision rule you can apply in five minutes
Run the entrypoint with node --no-strip-types disabled — that is, just run it — and see what throws. The errors are specific and the list is short. Beyond that:
- Good fit: CLI tools, scripts, build tooling, small HTTP services, Lambda-style handlers, anything where the build step exists only to remove types.
- Bad fit: decorator-based frameworks, codebases with enums everywhere, anything with
.tsx, anything relying on path aliases you cannot rewrite. - Either way: you still need
tsc --noEmitin CI. Node erases types without checking them, so a stripped file with a type error runs happily until it does not.
That last point is the one that gets lost in the excitement. Removing the build step does not remove type checking; it moves it entirely into your editor and your pipeline. If your CI currently gets type safety as a side effect of the build, dropping the build drops the safety with it.
Schema-level tooling helps here, because the boundary between untyped input and typed code is where erasure bites hardest. Generating types from real payloads with a JSON-to-TypeScript converter and validators with a JSON-to-Zod generator keeps runtime validation and static types in sync without adding a compile step. The same goes for quick conversions like turning a curl command into fetch code when you are stubbing a client; the rest of the browser-based dev tools run without installing anything.
The rest of Node 26 lands at the same time
If you are planning the upgrade, type stripping is not the only thing in the box. Node 26 enables the Temporal API by default, ships V8 14.6 and undici 8.0.2, and bumps NODE_MODULE_VERSION to 147, which means every native addon needs rebuilding. The build requirements moved too: GCC 13.2 minimum, and Python 3.9 support dropped.
The removals are the part to grep for. The legacy stream internals — _stream_readable, _stream_writable and the rest — are gone, as is http.Server.prototype.writeHeader. Node 26 is a Current release scheduled to become LTS in October 2026, so most teams will meet all of this at once, on the LTS bump, rather than now.
Weighing a runtime change like this is the same exercise as comparing any two developer tools: the marketing claim is “no build step”, and the thing that decides it is a five-item compatibility list. Plenty of developer tooling and open-source projects have already made the jump for scripts while keeping a compiler for their application code, which is the sensible middle.
FAQ
Which Node version runs TypeScript without a flag?
Type stripping has been enabled by default since Node 22.18.0 and 23.6.0, and is marked stable from 24.12.0 and 25.2.0 onward. You do not need a flag on any current release.
What did Node 26 remove?
Node 26.0.0 removed the --experimental-transform-types flag. Node now only erases types; it will not transform TypeScript syntax that requires emitting runtime JavaScript.
Why does my enum throw ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX?
An enum declaration compiles to a runtime object, and type stripping only removes annotations. Replace it with a const object asserted as const and a union type derived from its values.
Does Node read tsconfig.json?
No. Node ignores tsconfig.json entirely, including paths aliases. Use the imports field in


