DEV Community

AttractivePenguin
AttractivePenguin

Posted on

Node.js 24 Ships Native TypeScript: The End of Build Steps?

Node.js 24 Ships Native TypeScript: The End of Build Steps?

It's finally here. After years of requiring TypeScript compilation before execution, Node.js 24 lets you run .ts files directly. But does this mean you can throw away your bundler? Lets dive in.


The Big Picture

Node.js 24 (recommended LTS as of March 2026) has landed with a feature that developers have wanted for over a decade: native TypeScript support. Using the --experimental-strip-types flag (now enabled by default for .ts files), you can execute TypeScript directly without any compilation step.

Combine this with npm v11s 65% faster installations, and the JavaScript/TypeScript development workflow has fundamentally changed.

# Before: You needed this mess
npm install -D typescript ts-node esbuild
npx tsc
node dist/app.js

# Now: Just this
node app.ts
Enter fullscreen mode Exit fullscreen mode

Thats it. Thats the headline. But theres more nuance you need to understand.


How Native TypeScript Actually Works

The Mechanism: Type Stripping, Not Type Checking

When Node.js executes a TypeScript file directly, it doesnt perform type checking—thats still TypeScripts job. Instead, Node uses a process called type stripping that removes all type annotations, interfaces, and type-only constructs before execution.

// app.ts - This is valid TypeScript
interface User {
  name: string;
  email: string;
  age: number;
}

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}

const newUser: User = {
  name: "Sarah",
  email: "sarah@example.com",
  age: 28
};

console.log(greet(newUser));
Enter fullscreen mode Exit fullscreen mode

When Node.js 24 runs this, it internally transforms it to:

// What Node actually executes
function greet(user) {
  return `Hello, ${user.name}!`;
}

const newUser = {
  name: "Sarah",
  email: "sarah@example.com",
  age: 28
};

console.log(greet(newUser));
Enter fullscreen mode Exit fullscreen mode

Enabling Native TypeScript

Option 1: Automatic (Node.js 24+)

Just run your .ts file:

node app.ts
Enter fullscreen mode Exit fullscreen mode

Node.js automatically detects .ts extension and enables type stripping. No flags needed.

Option 2: Explicit Flag

node --experimental-strip-types app.ts
Enter fullscreen mode Exit fullscreen mode

Option 3: Package.json Configuration

{
  "name": "my-app",
  "type": "module",
  "scripts": {
    "start": "node --experimental-strip-types src/index.ts",
    "dev": "node --watch --experimental-strip-types src/index.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

Running TypeScript with ES Modules

If youre using ES modules (import/export syntax), it works seamlessly:

// utils/calculator.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

// main.ts
import { add, multiply } from ./utils/calculator.js;

console.log(5 + 3 =, add(5, 3));
console.log(4 × 7 =, multiply(4, 7));
Enter fullscreen mode Exit fullscreen mode

Run it:

$ node main.ts
5 + 3 = 8
4 × 7 = 28
Enter fullscreen mode Exit fullscreen mode

Comparison: Traditional Build vs Native Execution

Development Workflow Comparison

Aspect Traditional Build Node.js 24 Native
First execution Compile then run Direct execution
Hot reload Rebuild + restart Node --watch
Type errors Build fails Still runs (runtime behavior)
Dependencies 5+ npm packages None extra
Startup time tsc + node Just node

The Real Speed Difference

Heres a benchmark comparing workflows:

// benchmark.ts - Simple HTTP server
import http from http;

const server = http.createServer((req, res) => {
  res.writeHead(200, { Content-Type: text/plain });
  res.end(Hello from Node.js 24!\n);
});

server.listen(3000, () => {
  console.log(Server running at http://localhost:3000/);
});
Enter fullscreen mode Exit fullscreen mode

Traditional workflow (2025):

$ time npx tsc && node dist/benchmark.js
real    0m2.341s  # tsc compilation time

$ time node --watch dist/benchmark.js  # Rebuild on change
Enter fullscreen mode Exit fullscreen mode

Node.js 24 workflow (2026):

$ time node --watch benchmark.ts
real    0m0.089s  # Just Node startup

$ time node --watch benchmark.ts  # Instant hot reload
Enter fullscreen mode Exit fullscreen mode

The difference? ~26x faster startup in development.


Migration Guide: Existing Projects

Step 1: Install Node.js 24

# Using nvm (recommended)
nvm install 24
nvm use 24

# Verify
node --version
# v24.0.0
Enter fullscreen mode Exit fullscreen mode

Step 2: Update package.json

{
  "scripts": {
    "start": "node server.ts",
    "dev": "node --watch server.ts",
    "build": "tsc",  // Keep for production builds
    "type-check": "tsc --noEmit"
  },
  "engines": {
    "node": ">=24.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure TypeScript for Production

You still need TypeScript compilation for production deployments—but now its only for production:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "stripInternal": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Handle Environment-Specific Code

// config.ts
const isProduction = process.env.NODE_ENV === production;

export const config = {
  // This still works in native TypeScript
  logLevel: isProduction ? warn : debug,

  // Conditional types - handled by type stripping
  ...(isProduction && {
    apiUrl: https://api.production.com
  })
};
Enter fullscreen mode Exit fullscreen mode

npm v11: The 65% Faster Installation

Node.js 24 ships with npm v11, which brings significant performance improvements:

# npm v10 (2025)
$ time npm install
real    0m8.432s

# npm v11 (2026)  
$ time npm install
real    0m2.951s
Enter fullscreen mode Exit fullscreen mode

Key improvements:

  • Parallel dependency resolution - dependencies install concurrently
  • Improved caching - smarter detection of cached packages
  • Reduced metadata overhead - less package.json parsing
# New npm 11 commands
npm install --parallel    # Force parallel installs
npm install --prefer-offline  # Use cached packages first
Enter fullscreen mode Exit fullscreen mode

Limitations and Caveats

What Native TypeScript Doesnt Do

No type checking at runtime

// This runs without error in Node.js 24
const user: User = "not a user";  // No runtime error!
Enter fullscreen mode Exit fullscreen mode

No type erasure at compile time for dist/
Type stripping happens at runtime, not build time. For production, you still need tsc.

No JSX transformation
React JSX still needs Babel or a bundler:

// This WONT work directly in Node.js 24
const element = <div>Hello</div>;  // Syntax error!
Enter fullscreen mode Exit fullscreen mode

No CSS modules, asset imports

// These still require a bundler
import styles from ./styles.css;
import image from ./logo.png;
Enter fullscreen mode Exit fullscreen mode

When to Keep Your Bundler

Use Case Native TypeScript Bundler Required
Backend API servers ✅ Yes Optional
CLI tools ✅ Yes Optional
Simple scripts ✅ Yes No
React/Vue frontend ❌ No Yes
Full-stack apps Partial Yes
Library/package publishing ❌ No Yes

Troubleshooting Common Issues

Error: "ERR_UNKNOWN_FILE_EXTENSION"

$ node app.ts
Error [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension: .ts
Enter fullscreen mode Exit fullscreen mode

Fix: Ensure youre using Node.js 24+:

node --version  # Must be v24.0.0 or higher
Enter fullscreen mode Exit fullscreen mode

Error: "Cannot find module"

import { helper } from ./utils/helper;  // No .js extension
Enter fullscreen mode Exit fullscreen mode

Fix: Add .js extension for ESM compatibility:

import { helper } from ./utils/helper.js;
Enter fullscreen mode Exit fullscreen mode

Types Not Available at Runtime

// type-utils.ts
export type Result<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

// This type information is stripped at runtime
function handleResult<T>(result: Result<T>): T {
  if (!result.success) {
    throw new Error(result.error);  // result.error might not exist!
  }
  return result.data;
}
Enter fullscreen mode Exit fullscreen mode

Fix: Use type guards for runtime validation:

function isSuccess<T>(result: Result<T>): result is { success: true; data: T } {
  return result.success;
}
Enter fullscreen mode Exit fullscreen mode

Mixed .js and .ts Projects

project/
├── src/
│   ├── legacy.js      # Still works
│   └── new.ts         # Uses native TypeScript
Enter fullscreen mode Exit fullscreen mode

Node.js 24 handles mixed extensions seamlessly—just run:

node --experimental-strip-types src/index.ts
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Migrating an Express API

Heres a complete migration story from traditional TypeScript to native:

Before (package.json - 2025)

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "ts-node-dev --respawn src/index.ts"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "ts-node-dev": "^2.0.0",
    "@types/node": "^20.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

After (package.json - 2026)

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "node --watch --experimental-strip-types src/index.ts"
  },
  "devDependencies": {
    "typescript": "^5.7.0",  // Still needed for type-checking
    "@types/node": "^22.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

The Code (src/index.ts)

import express from express;

interface AppConfig {
  port: number;
  env: string;
}

const config: AppConfig = {
  port: parseInt(process.env.PORT || 3000),
  env: process.env.NODE_ENV || development
};

const app = express();

app.get(/health, (req, res) => {
  res.json({ status: ok, timestamp: new Date().toISOString() });
});

app.get(/api/users, (req, res) => {
  const users = [
    { id: 1, name: Alice, role: admin },
    { id: 2, name: Bob, role: developer }
  ];
  res.json(users);
});

app.listen(config.port, () => {
  console.log(`Server running in ${config.env} mode on port ${config.port}`);
});

export default app;
Enter fullscreen mode Exit fullscreen mode

Run it:

npm run dev
# Server running in development mode on port 3000
Enter fullscreen mode Exit fullscreen mode

Development is instant. Production builds still work:

npm run build && npm start
# Same result, optimized bundle
Enter fullscreen mode Exit fullscreen mode

FAQ

Q: Is Node.js 24 LTS now?

A: Yes, as of March 2026, Node.js 24 is the recommended LTS version for new projects.

Q: Can I delete my node_modules/.bin/tsc?

A: No! You still need TypeScript for:

  • Type checking (tsc --noEmit)
  • Declaration file generation
  • Production builds with optimization

Q: Does this work with Deno?

A: Deno has had native TypeScript for years. This is Node.js catching up. The key difference is Node.js 24 maintains backward compatibility with the existing npm ecosystem.

Q: What about type definitions (.d.ts)?

A: They still work! Node.js 24 respects @types packages for IntelliSense and type checking.

Q: Can I use this in production?

A: For development: absolutely. For production, its still recommended to use traditional builds with optimization (minification, tree-shaking). Native TypeScript is primarily a development experience improvement.

Q: Does this replace ts-node?

A: Yes, for most use cases. ts-node was a workaround for exactly this feature. You can remove it from your dependencies.

Q: What about Jest and testing?

A: Jest still needs configuration for TypeScript (via ts-jest or swc). Test execution can use native TypeScript, but test configuration still typically requires compilation.

Q: Can I use this with Docker?

A: Yes! Update your Dockerfile:

FROM node:24-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "--experimental-strip-types", "src/index.ts"]
Enter fullscreen mode Exit fullscreen mode

The Bigger Picture: Why This Matters

This isnt just a convenience feature—it signals a shift in how we think about JavaScript tooling:

  1. Build tools become optimization tools, not required infrastructure
  2. TypeScript moves from compilation to interpretation
  3. Development loop tightens from seconds to milliseconds
  4. Lower barrier to entry for new JavaScript developers

The ecosystem will evolve. Some bundlers will become optimizers. Some will fade away. But for now, enjoy the simplest TypeScript developer experience in the languages history.


Conclusion

Node.js 24s native TypeScript support is a game-changer for development workflows. While it wont replace bundlers for production builds, it dramatically simplifies the development experience:

  • No more ts-node or tsx for simple execution
  • 65% faster npm installs with npm v11
  • Instant hot reload with --watch
  • Zero extra dependencies for TypeScript execution

Your move: Update to Node.js 24, remove ts-node from devDependencies, and enjoy the simpler workflow.

nvm install 24
nvm use 24
node --watch app.ts
Enter fullscreen mode Exit fullscreen mode

Welcome to 2026. The build step is (mostly) dead. Long live TypeScript.


Tags: javascript,typescript,nodejs,programming,tutorial,webdev,devops

Top comments (0)