
How to Run TypeScript Directly in Node.js Without a Transpilation Step
The era of complex build pipelines is receding as Node.js introduces native type-stripping, allowing you to execute .ts files with zero external dependencies.
For years, the collective wisdom of the web development community has been that Node.js cannot execute TypeScript. We’ve been conditioned to believe that a build step is a fundamental law of nature—like gravity, or the fact that your CSS will definitely break on Safari. If you wanted to run .ts files, you had to invite tsc, babel, ts-node, or esbuild to the party.
But as of Node.js v22.6.0, that "requirement" has been relegated to the bin. You can now run TypeScript directly in Node.js with zero external dependencies and zero manual transpilation.
The Magic Flag: --experimental-strip-types
Node.js recently introduced a feature called type stripping. It isn't a full-blown TypeScript compiler living inside the Node binary. Instead, it’s more like a pair of digital scissors. It scans your file, snips out the type annotations, and executes the resulting JavaScript.
Here is the "Hello World" of this new era. Create a file named index.ts:
interface User {
id: number;
name: string;
}
const greet = (user: User): string => {
return `Hello, ${user.name}! Your ID is ${user.id}.`;
};
const me: User = { id: 1, name: "Node Enthusiast" };
console.log(greet(me));Normally, running node index.ts would result in a SyntaxError because Node wouldn't know what to do with that interface or those colon-separated types. But now, you can run:
node --experimental-strip-types index.tsIt just works. No node_modules folder full of build tools, no tsconfig.json to misconfigure, and no waiting for a build pipeline to warm up.
Why this changes things
I’ve spent far too many hours of my life debugging tsconfig.json files that refused to cooperate with ESM modules. For small scripts, CLI tools, or quick prototypes, the overhead of setting up a build pipeline feels like bringing a chainsaw to a knife fight.
The native type-stripping approach is focused purely on Developer Experience (DX). It’s about getting from "I have an idea" to "The code is running" as fast as possible.
What it *doesn't* do (The Gotchas)
It is important to understand that Node.js is stripping types, not checking them.
1. No Type Checking: Node doesn't care if you pass a string where a number is expected. It won't yell at you. If you want actual type safety, you still need to run tsc --noEmit in your CI or your editor.
2. No Enums or Namespaces: These are "extra" features of TypeScript that actually require generating new JavaScript code (transpilation), not just removing types. Because Node is only *stripping*, it will throw an error if it encounters an enum.
3. Strictly Experimental: It’s in the flag name. Things might change. Don't go refactoring your bank's core infrastructure to use this just yet.
Working with Dependencies
You can even use this with external libraries. Let’s say you want to use the built-in node:fs module with types:
import { readFileSync } from 'node:fs';
function getPackageName(): string {
const content: string = readFileSync('./package.json', 'utf8');
const pkg: { name: string } = JSON.parse(content);
return pkg.name;
}
console.log(`Current project: ${getPackageName()}`);If you run this with the flag, Node handles the imports and the types perfectly.
Dealing with Enums (The Workaround)
Since enum is a no-go, I've found myself leaning back into the "old ways" which, honestly, are often better anyway. If you need a set of constants, use a POJO (Plain Old JavaScript Object) with as const.
This will fail:
enum Status {
Pending,
Success
}This will work:
const Status = {
Pending: 0,
Success: 1,
} as const;
type Status = typeof Status[keyof typeof Status];By using the as const pattern, you get the same type safety in your IDE, but the code remains "strippable" because the object itself is valid JavaScript.
Speed and Performance
Because there is no heavy transformation happening—just a regex-like removal of type syntax—it’s incredibly fast. In my local tests, scripts start up almost as quickly as pure JS.
Compare this to ts-node, which often feels like it's taking a nap before it decides to execute your code. Even tsx (which is fantastic and uses esbuild under the hood) is an extra dependency you no longer *strictly* need for basic tasks.
Setting it as a Default
If you’re like me and hate typing long flags, you can set an environment variable in your .zshrc or .bashrc:
export NODE_OPTIONS="--experimental-strip-types"Now, you can just run node index.ts and feel like you're living in the year 2030.
When should you still use a Build Step?
Native type stripping is great for:
* Quick scripts and automation.
* Server-side logic where you don't want a dist folder.
* Learning TypeScript without the "configuration tax."
You should stick to tsc or esbuild for:
* Frontend code: Browsers have zero intention of supporting native TS stripping anytime soon.
* Production apps: If you use decorators or enums heavily, you'll need a real compiler.
* Performance at scale: For massive applications, a pre-compiled JS bundle is still the gold standard.
Node.js taking this step is a massive win for the ecosystem. It acknowledges that TypeScript is the *de facto* language of the web, and it's finally making it a first-class citizen. Go ahead, delete that tsconfig.json for your next helper script. It feels great.


