Running TypeScript Directly with Node.js: No Build Step Required!
Node.js now supports running TypeScript directly by stripping types at runtime, eliminating the need for a build step. TypeScript 5.8 introduces the erasableSyntaxOnly flag to ensure compatibility. This may streamline workflows and speed up development, although there are limitations to be aware of.

TypeScript has become the go-to language for JavaScript developers who want type safety and improved tooling. However, one common frustration has been the need for a build step to compile TypeScript to JavaScript before running it. Today, we'll explore the exciting new developments that allow you to run TypeScript directly with Node.js, eliminating that extra compilation step.
The Traditional Way vs. A New Way
Traditionally, working with TypeScript required:
- Writing your TypeScript code
- Compiling it to JavaScript using
tsc
or another build tool - Running the compiled JavaScript with Node.js or another JS runtime
This workflow adds complexity and slows down the development process. But thanks to recent advancements in both Node.js and TypeScript, we can now run TypeScript files directly!
Node.js TypeScript Support: Behind the Scenes
Node.js 23.6 unflagged experimental support for running TypeScript files directly. This feature works by stripping types from your TypeScript code at runtime, leaving valid JavaScript that Node.js can execute.
This approach uses a library called Amaro, a wrapper around @swc/wasm-typescript
, a WebAssembly port of the SWC TypeScript parser. It's important to note that this differs from full TypeScript compilation - it simply removes the type annotations without performing type checking, a process known as Type Stripping.
TypeScript's New --erasableSyntaxOnly
Flag
With TypeScript 5.8, the TypeScript team introduced a new compiler flag called --erasableSyntaxOnly
. This flag restricts your code to only use TypeScript features that can be cleanly erased, giving you immediate feedback if something won't work with type-stripping approaches, such as the one Node.js uses.
When enabled, this flag will cause TypeScript to raise errors for:
enum
declarationsnamespace
s andmodule
s with runtime code- Parameter properties in classes (e.g.,
constructor(private name: string)
) - Non-ECMAScript
import =
andexport =
assignments
For example, this code would trigger errors with --erasableSyntaxOnly
enabled:
// Error! Not allowed with erasableSyntaxOnly
enum Direction {
Up,
Down,
Left,
Right,
}
// Error! Not allowed with erasableSyntaxOnly
namespace Container {
export const value = 42;
}
// Error! Not allowed with erasableSyntaxOnly
import Bar = container.Bar;
class Person {
// Error! Not allowed with erasableSyntaxOnly
constructor(private name: string, public age: number) {}
}
Decorator Restrictions
When using Node.js's native TypeScript support, it's important to understand that decorators have significant limitations. Since Node.js is only stripping types without performing transformations, decorators present a special challenge.
Decorators are currently a TC39 Stage 3 proposal and aren't fully supported by JavaScript engines yet. Using Node.js's type-stripping approach, decorators will result in a parser error. This is because decorators require actual code transformation or JS runtime support, thus making them incompatible with type erasure.
Slight differences between TypeScript decorators and the JavaScript proposal further complicate this issue.
For example, this code using decorators won't work with Node.js's type-stripping approach:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
As the JavaScript decorator proposal progresses and eventually becomes part of the language standard, Node.js will support decorators in TypeScript when supported in JavaScript. Until then, if your project relies heavily on decorators, you must continue using traditional TypeScript compilation workflows.
Setting Up a Project to Run TypeScript Directly
Let's set up a simple project that runs TypeScript directly with Node.js:
- First, create a new directory and initialize a Node.js project:
mkdir ts-direct
cd ts-direct
npm init -y
- Install TypeScript as a development dependency:
npm install --save-dev typescript
- Create a TypeScript configuration file (
tsconfig.json
) with the new flag:
touch tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
},
"include": ["src/**/*"]
}
- Create a source directory and a simple TypeScript file:
mkdir src
touch src/index.ts
- Add a simple TypeScript program to
src/index.ts
:
// Define types that will be erased at runtime
type Status = "pending" | "success" | "error"
// A function with type annotations
function processItem(id: number, status: Status): string {
return `Item ${id} is ${status}`
}
// Using the function with proper types
const result = processItem(42, "success")
console.log(result)
// Define a simple class with types
class Item {
constructor(
id: number, // Notice: no access modifiers here!
status: Status
) {
this.id = id
this.status = status
}
id: number
status: Status
toString(): string {
return `Item(${this.id}, ${this.status})`
}
}
const item = new Item(123, "pending")
console.log(item.toString())
- Update your
package.json
to add a start script:
{
"scripts": {
"start": "node src/index.ts"
}
}
- Now you can run your TypeScript directly:
npm start
npm start
> ts-direct@1.0.0 start
> node src/index.ts
Item 42 is success
Item(123, pending)
(node:78280) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Alternative Tools
While Node.js native support is exciting, other established tools exist for directly running TypeScript. A few of these are:
- ts-node: A TypeScript execution environment for Node.js allows you to run TypeScript code directly.
- tsx: Another TypeScript execution environment that doesn't perform type checking.
- esbuild-runner: Uses esbuild for super-fast on-the-fly transpilation of TypeScript.
- esbuild-node-tsc: Builds your TypeScript Node.js projects using esbuild.
Benefits and Future Outlook
Running TypeScript directly with Node.js offers several advantages:
- Faster development cycles: No need to wait for compilation
- Simpler setup: Fewer tools and configuration needed
- Easier debugging: Debug your TypeScript code directly
This approach also aligns with the "types as comments" proposal for JavaScript, which would allow JavaScript to support type annotations that are safely ignorable at runtime. This could eventually lead to TypeScript support directly in browsers!
Conclusion
The ability to run TypeScript directly with Node.js is a significant step forward for developer experience. By leveraging the new --erasableSyntaxOnly
flag in TypeScript 5.8 and Node.js's experimental type-stripping capabilities, we can simplify our workflows while maintaining the benefits of TypeScript's type system.
As these features mature, we can look forward to an even more seamless integration between TypeScript and JavaScript runtimes, potentially bringing us closer to the dream of running TypeScript in the browser without compilation.