Node.js 22.18.0 — TypeScript by Default
Node.js 22.18.0 makes type stripping the default for TypeScript files, removing the experimental flag and requiring no extra tooling to run .ts files directly.

The assumption that TypeScript requires a build step before Node.js can run it has been load-bearing for years. Entire categories of tooling —
ts-node,tsx,esbuild-based runners,tsc --watchpipelines — exist to work around it. Node.js 22.18.0 removes the assumption, not the tools.
Source
By: Antoine du Hamel (@aduh95)
Published: Jul 31 2025
Type: Release notes
Saved because this is the LTS release where type stripping becomes the default, which is the moment it becomes a reasonable baseline to design toolchains around rather than a feature to evaluate.
What Type Stripping Means in Practice
Type stripping is not compilation. The distinction is worth holding precisely. When Node.js runs a .ts file in 22.18.0, it removes TypeScript-specific syntax — annotations, interfaces, type parameters — and executes the resulting JavaScript. It does not run the TypeScript type-checker. If a type annotation is wrong, Node.js does not know and does not care. If there is a runtime logic error that TypeScript’s type system would have caught, type stripping offers no protection. The value is ergonomics: scripts, tools, and development workflows that previously required an extra step or an extra dependency now run with node file.ts.
The limitations are specific and documented at nodejs.org/api/typescript.html#type-stripping. Type-only imports require import type syntax. If a bare import { Foo } imports something that is only a type, the stripped output may leave behind a module reference that errors at runtime. Regular enum declarations produce JavaScript objects and are supported, but const enum inlining is handled at strip time, not by a separate compilation pass. The older experimentalDecorators behavior is not supported; only TC39 stage-3 decorator semantics work. Namespace merging is unsupported. These limitations are not bugs — they are the boundaries of what stripping can do without a full compilation step, and understanding them determines whether a given TypeScript codebase can use this feature without modification.
The feature is marked experimental and subject to change, but “experimental” at this stage in the release cycle means the behavior is under active refinement, not that it is unstable for general use. The --no-experimental-strip-types flag opts out entirely if the feature conflicts with an existing toolchain. The implementation is backed by amaro, which reached 1.1.0 in this release. The commit log shows amaro cycling through 0.5.0 through 0.5.3, then 1.0.0, then 1.1.0 within the same pull request sequence — a version history that reflects how much iteration happened between the initial experimental flag and the unflagged default. The --experimental-strip-types flag that existed in earlier Node.js 22 releases still works but is no longer required.
import.meta.main arrives in the same release and is worth noting alongside type stripping because the two features together make Node.js significantly more ergonomic for TypeScript scripts. import.meta.main is the ESM-native way to check whether the current module is the process entry point — the pattern previously expressed as require.main === module in CommonJS. Bun added import.meta.main earlier; Node.js now aligns with that. The CommonJS pattern has edge cases in mixed CJS/ESM codebases, particularly when modules are loaded through node:vm, that import.meta.main avoids cleanly. For script authors who want a TypeScript file to behave differently when run directly versus when imported, this is the correct idiom going forward.
Two permission model changes ship in this release that matter for security-conscious deployments. Permission flags now propagate to spawned child processes — a process running under a restricted permission scope can no longer spawn a child with broader access by omitting the flags. permission.has("addon") adds introspective capability so that code can check whether native addon loading is permitted before attempting it and failing. The --watch-kill-signal flag addresses a practical gap in development workflows: the signal used to terminate the watched process on file change was previously fixed, which caused problems for processes that need graceful shutdown on SIGTERM rather than an abrupt kill. Both changes are incremental, but they close real gaps in production and development tooling.
The SQLite module gains a readBigInts option at the connection level, complementing the per-query safeIntegers option that existed previously. fs event handling with AsyncIterator gets improved burst handling to avoid dropped events when many filesystem changes arrive simultaneously. Worker threads become async disposable via Symbol.asyncDispose, which means worker lifecycle in tests and scripts can be managed with await using syntax rather than explicit cleanup.
What I want to remember
Remember: Type stripping is opt-out, not opt-in, as of 22.18.0 LTS — if a TypeScript codebase uses
const enum,experimentalDecorators, or namespace merging, it will need either--no-experimental-strip-typesor migration work before upgrading.