← Back to BlogsBLOGWeb Development

Run TypeScript Directly in Node.js: Type Stripping and the One Big Catch

Node.js 24 runs TypeScript directly, no ts-node and no build step, via type stripping. The catch is it erases types rather than checking them. Here is the safe workflow.

about 7 hours ago
❤️ 0 likes💬 0 comments
developer tools
Run TypeScript Directly in Node.js: Type Stripping and the One Big Catch

You can run a TypeScript file with node app.ts and nothing else. No ts-node, no tsx, no build step, no tsconfig. Node.js 24, the current LTS, ships native TypeScript support switched on by default, and it is stable now, not experimental. It is a genuine quality-of-life win. It is also easy to misunderstand, because of what it does not do.

Key takeaways

  • On Node.js 24 LTS, node app.ts runs directly, no ts-node, no build step, no flag.
  • It works by stripping type annotations before V8 runs the code, not by compiling.
  • The catch: stripping does not type-check. Keep tsc --noEmit in CI to catch type errors.

How it works: stripping, not compiling

Node does not compile your TypeScript. It strips it. Before V8 ever sees your code, Node uses Amaro (a thin wrapper around SWC's WebAssembly TypeScript parser) to remove the type annotations, then runs the plain JavaScript that is left. A nice detail: the stripped syntax is replaced with whitespace rather than deleted, so line and column numbers in stack traces still line up with your source.

The feature arrived in stages. Node 22 introduced it behind --experimental-strip-types, Node 23 unflagged it, and Node 24 makes it the default for any .ts file, stable, and available in the Node 22 LTS line as well (from 22.18). On Node 24 you need no flag at all. The official behavior is documented in the Node.js TypeScript modules docs.

Try it yourself

On Node 24 there is no setup. Save this as greet.ts:

TypeScript
function greet(name: string): string {  return `Hello, ${name}!`;}console.log(greet("Node 24"));

Run it directly, no tsc, no ts-node, no config:

Bash
$ node --versionv24.20.0$ node greet.tsHello, Node 24!

Now the part that surprises people. Save broken.ts, where port is typed as a number but assigned a string:

TypeScript
const port: number = "3000";console.log(`Listening on port ${port}`);

Node runs it anyway, because it strips the : number annotation instead of checking it:

Bash
$ node broken.tsListening on port 3000

The compiler would have stopped you:

Bash
$ npx tsc --noEmit broken.tsbroken.ts(3,7): error TS2322: Type 'string' is not assignable to type 'number'.

That gap between "it runs" and "it is correct" is the whole story.

The one big catch: it does not type-check

This is the part to internalize. Stripping is whitespace-only, and Node never validates your types. node app.ts will happily run code that tsc would reject. Native execution replaces your build step, not your type checker.

The practical consequence: keep running tsc --noEmit in CI. That is where type errors get caught now. Type stripping speeds up running and iterating; it does not remove the need to type-check before you ship.

What is not supported

Because it only strips, a few things do not work out of the box:

  • Enums and namespaces need real code generation, not stripping. Node had an --experimental-transform-types flag for them, but that flag is being removed, so the pragmatic move is to avoid enums and namespaces in files you run directly, or precompile those.
  • tsconfig.json is ignored. Path aliases and anything else you configure there do not apply.
  • No JSX. .tsx files are not handled.

When to use it

Native stripping shines for scripts, tooling, tests, and server entry points where a build step was pure friction. Cold starts get dramatically faster too: on a real Express server, moving from ts-node to native stripping took cold start from about 1.4 seconds to roughly 200 milliseconds, about 7 to 9 times faster. For a production build where you want declaration files, bundling, and full type checking, you still reach for tsc or your bundler.

Pair it with the compiler, not instead of it. Node runs your TypeScript, and tsc --noEmit still tells you whether it is correct. On the compiler side, the checker itself just got much faster: see TypeScript 7.0's Go rewrite.

Join the discussion on Run TypeScript Directly in Node.js: Type Stripping and the One Big Catch

Likes, comments, and replies are available for authenticated readers with verified email addresses.

Comments (0)

Loading discussion...

Related articles