TypeScriptRunning TS (ts-node, tsx, bundlers)

Running TypeScript (ts-node, tsx, Bundlers)

By default, Node.js cannot execute .ts files directly — you first compile them to JavaScript with tsc, then run the .js output. That two-step process is fine for production builds, but tedious during development.

Several tools let you run TypeScript directly without a manual compile step. This page covers every major approach: from the venerable ts-node to the modern tsx, from bundlers like Vite and webpack to the native Node.js 22.6+ flag.

The Options at a Glance

Tool

Type-checks?

ESM support

Speed

Best for

tsc + node

Yes

Yes

Slow (full compile)

Production builds

ts-node

Yes (default)

Via --esm flag

Moderate

Scripts that need type safety

ts-node --transpileOnly

No

Via --esm flag

Fast

Quick scripts

tsx

No

Yes

Very fast (esbuild)

Development scripts, CLIs

esbuild

No

Yes

Extremely fast

Bundling and transpilation

Vite

No (but tsc --noEmit)

Yes

Very fast (esbuild/Rollup)

Frontend apps

webpack + ts-loader

Yes

Yes

Moderate

Complex frontend builds

Next.js

No (but tsc --noEmit)

Yes

Fast (SWC)

Full-stack React apps

Bun

No

Yes

Extremely fast

Fast scripts and servers

Node.js 22.6+ --experimental-transform-types

No

Yes

Fast (built-in)

Native Node experiments

ts-node

ts-node is the original TypeScript execution engine for Node.js. It hooks into Node's module loader and compiles .ts files on-the-fly using the TypeScript compiler. By default it performs full type-checking, which makes it slower than newer alternatives — but it is the safest option for scripts where you want compile-time guarantees.

Bash
# Install
npm install --save-dev ts-node typescript @types/node

# Run a TypeScript file directly
npx ts-node src/index.ts

# Run with the REPL (interactive TypeScript shell)
npx ts-node
$ npx ts-node src/greet.ts
Hello from TypeScript!

ts-node respects your tsconfig.json. The most useful options:

Bash
# Skip type-checking (just transpile — much faster)
npx ts-node --transpile-only src/index.ts

# Use a specific tsconfig
npx ts-node --project tsconfig.scripts.json src/index.ts

# Pass TypeScript compiler options inline
npx ts-node --compiler-options '{"module":"commonjs"}' src/index.ts

Add ts-node to your package.json scripts:

JSON
{
  "scripts": {
    "start": "ts-node src/index.ts",
    "start:fast": "ts-node --transpile-only src/index.ts",
    "dev": "ts-node --watch src/index.ts"
  }
}
ts-node with ESM

By default ts-node targets CommonJS. To use ES Modules you need the --esm flag and a matching tsconfig.json:

JSON
// tsconfig.json for ESM
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "node16",
    "moduleResolution": "node16"
  }
}

Bash
npx ts-node --esm src/index.ts

Alternatively, use the ESM loader directly:

Bash
node --loader ts-node/esm src/index.ts
Note
If your package.json has "type": "module", you are in ESM mode. Make sure your tsconfig.json uses"module": "node16" or "nodenext" and that your imports include the .js extension (TypeScript resolves these to .tsat compile time).
tsx — The Faster Alternative

tsx is a drop-in replacement for ts-node that uses esbuild under the hood instead of tsc. The result is dramatically faster startup:

  • ts-node: ~2–5 seconds on a large project
  • tsx: ~50–200ms on the same project

The tradeoff: tsx does not type-check. It just strips the TypeScript syntax and runs the code. This is usually fine for development, where your editor provides real-time type feedback.

Bash
# Install
npm install --save-dev tsx

# Run a TypeScript file
npx tsx src/index.ts

# Watch mode — restarts when files change
npx tsx watch src/index.ts

# Use as a shebang in scripts
#!/usr/bin/env npx tsx

JSON
{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "start": "tsx src/index.ts",
    "type-check": "tsc --noEmit"
  }
}
Tip
The recommended pattern with tsx: use tsx for running code (fast), and a separate tsc --noEmit script for type-checking. Run the type check in CI and pre-commit hooks.
tsx vs ts-node — Feature Comparison

Feature

ts-node

tsx

Under the hood

TypeScript compiler (tsc)

esbuild

Type checking

Yes (default)

No

Startup speed

Moderate (~2-5s)

Very fast (~50-200ms)

ESM support

Via --esm flag

Native, out of the box

Watch mode

Via nodemon

Built-in (tsx watch)

tsconfig respect

Full

Partial (paths, target)

Decorators

Yes

Yes (esbuild handles them)

Install size

Larger (bundles tsc)

Smaller (bundles esbuild)

TypeScript with webpack

For frontend projects not using Vite, webpack + ts-loader is a proven combination:

Bash
npm install --save-dev webpack webpack-cli ts-loader typescript

JS
// webpack.config.js
module.exports = {
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

ts-loader uses the TypeScript compiler, so you get full type-checking during builds. For faster builds without type-checking, use esbuild-loader or babel-loader with @babel/preset-typescript instead.

TypeScript with Vite

Vite has first-class TypeScript support built in — no configuration needed. Create a Vite + TypeScript project:

Bash
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

Vite uses esbuild to transpile TypeScript (fast, no type checking) during development. For production builds, it uses Rollup. Add a separate type-check step:

JSON
{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "type-check": "tsc --noEmit",
    "preview": "vite preview"
  }
}

Vite respects tsconfig.json for path aliases and module settings. The typical Vite tsconfig for a React project:

JSON
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}
TypeScript with esbuild

esbuild is an extremely fast JavaScript/TypeScript bundler and transpiler written in Go. It does not type-check — it strips TypeScript syntax and outputs JavaScript.

Bash
npm install --save-dev esbuild

Bash
# Bundle and transpile to a single file
npx esbuild src/index.ts --bundle --outfile=dist/index.js

# Target a specific platform
npx esbuild src/index.ts --bundle --platform=node --outfile=dist/index.js

# Watch mode
npx esbuild src/index.ts --bundle --watch --outfile=dist/index.js

JSON
{
  "scripts": {
    "build": "esbuild src/index.ts --bundle --platform=node --outfile=dist/index.js",
    "dev": "esbuild src/index.ts --bundle --platform=node --watch --outfile=dist/index.js",
    "type-check": "tsc --noEmit"
  }
}
TypeScript with Next.js

Next.js has built-in TypeScript support — just create .ts or .tsx files. Next.js uses SWC (a Rust-based compiler) to transpile TypeScript extremely fast.

Bash
# Create a new Next.js project with TypeScript
npx create-next-app@latest my-app --typescript

# Or add TypeScript to an existing Next.js project
# Just rename a file to .ts or .tsx and run:
npm run dev
# Next.js will auto-create tsconfig.json and install @types/react

Next.js transpiles TypeScript but does not block builds on type errors by default. Add type checking to your CI pipeline:

JSON
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "type-check": "tsc --noEmit"
  }
}
Bun — Native TypeScript Runner

Bun is a modern JavaScript runtime (like Node.js) that runs TypeScript natively without any extra tools or configuration:

Bash
# Install Bun
curl -fsSL https://bun.sh/install | bash

# Run TypeScript directly
bun run src/index.ts

# Watch mode
bun --watch run src/index.ts

# Run a script from package.json
bun run dev

Bun uses JavaScriptCore (Safari's engine) and transpiles TypeScript with its own built-in transpiler — extremely fast, no type-checking. It also handles npm packages and has a built-in test runner.

Tip
Bun is a great choice for TypeScript scripts, CLIs, and APIs where you want maximum startup speed. It is production-ready as of Bun 1.0 (September 2023).
Node.js 22.6+ — Built-in TypeScript Support

Node.js 22.6 introduced experimental support for running TypeScript files directly without any external tools. Node 23+ makes this more stable:

Bash
# Node.js 22.6+
node --experimental-transform-types src/index.ts

# Node.js 23+ (more stable, still experimental)
node --experimental-strip-types src/index.ts

Important limitations of the built-in Node.js TypeScript support:

  • Only strips types — no type-checking
  • Does not support TypeScript-specific syntax like enum or namespace with --experimental-strip-types (use --experimental-transform-types for those)
  • Decorators and parameter properties may not work
  • Still marked experimental — do not use in production yet
Warning
The Node.js native TypeScript flags are experimental as of Node.js 22–23. For production workloads, use tsx, ts-node, or compile with tsc first.
Tradeoffs Summary

The fundamental tradeoff in every TypeScript runner is type safety vs speed:

  • Full type-checking (tsc, ts-node default): slower but catches type errors before runtime. Use for CI, production builds, and scripts where correctness matters.
  • Transpile-only (tsx, esbuild, Vite dev, Bun, Next.js): extremely fast but no type safety at run time. Use for development and when your editor/IDE provides real-time type feedback. Always pair with a separate tsc --noEmit in CI.

The best production setup for most projects:

JSON
{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "type-check": "tsc --noEmit",
    "lint": "eslint src/**/*.ts"
  }
}
  • dev — tsx (fast iteration, no type-check overhead)

  • build — tsc (full compile + type check for production)

  • start — plain node (run compiled output)

  • type-check — tsc --noEmit (run in CI and pre-commit)

Choosing the Right Tool
  1. Next.js / Vite project? — TypeScript is built in. Add tsc --noEmit for CI.

  2. Node.js API or CLI? — Use tsx for development, tsc for production builds.

  3. One-off scripts? — tsx or bun run are simplest.

  4. Need full type checking at runtime? — ts-node (default mode).

  5. Monorepo with complex build? — tsc with project references.

  6. Maximum build speed? — esbuild or bun.

Success
You now know every major way to run TypeScript — from ts-node with full type checking, to tsx for fast development, to bundlers like Vite and webpack, to native Bun and Node.js support. The pattern to remember: use a fast transpile-only tool for development, and run tsc --noEmitin CI to catch type errors.