cheat sheet

ts-node

Daily-driver reference for the ts-node CLI — REPL, transpile-only mode, ESM loader, common script patterns.

ts-node — CLI

What it is

ts-node is the original TypeScript-Execute CLI for Node.js. It runs .ts files by invoking the real TypeScript compiler via the TS API, then handing the result to Node. New projects typically prefer tsx (faster, simpler ESM), but ts-node remains the right choice when you need genuine TS type-checking at runtime, the TS-aware REPL, paths aliases handled by the compiler, or emitDecoratorMetadata for legacy NestJS / TypeORM stacks.

For deep package context (peer deps, alternatives, security), see packages-npm/npm-ts-node. This page covers daily CLI use.

Install

bash
npm install -D ts-node typescript

Output: binaries ts-node, ts-node-esm, ts-node-script, ts-node-transpile-only in node_modules/.bin/.

Day-to-day commands

CommandWhat it does
ts-node file.tsCompile + run a TS file with full type-checking
ts-node --transpile-only file.tsSkip type-checking (faster, like tsx)
ts-nodeLaunch the TypeScript REPL
ts-node-esm file.tsRun in ESM mode
ts-node --esm file.tsSame as above, flag form
ts-node --swc file.tsUse SWC transpiler instead of tsc (skips types)
ts-node -r tsconfig-paths/register file.tsResolve TS paths aliases
node --loader ts-node/esm file.tsUse ts-node as a Node ESM loader

Common scenarios

Launch the TypeScript REPL

bash
ts-node

Output:

text
> const greet = (name: string) => `Hi, ${name}`
undefined
> greet("Alice")
'Hi, Alice'
> greet(42)
TSError: ⨯ Unable to compile TypeScript:
file.ts:1:11 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

The REPL respects tsconfig.json strict, paths, lib. Still the best interactive TS environment.

Run a script in transpile-only mode

bash
ts-node --transpile-only ./scripts/seed.ts

Output:

text
[seed] inserted 1000 rows

--transpile-only skips the type checker — roughly 5× faster. Pair with tsc --noEmit in a pre-commit hook so type errors don't disappear.

ESM mode with top-level await

bash
ts-node-esm ./scripts/migrate.ts

Output:

text
connected
3 migrations applied
typescript
// scripts/migrate.ts
import { Client } from "pg";
const db = new Client();
await db.connect();
console.log("connected");

Output:

text
(node:12345) ExperimentalWarning: --experimental-loader may be removed in the future...
connected

The experimental warning is harmless. New projects should prefer tsx for ESM scripts.

Resolve paths aliases

json
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  }
}
bash
npm install -D tsconfig-paths
ts-node -r tsconfig-paths/register ./src/server.ts

Output:

text
added 1 package in 1s
Server listening on http://localhost:3000

Or wire it into config:

json
{
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  }
}

Then ts-node ./src/server.ts picks it up automatically.

Use SWC for speed

bash
npm install -D @swc/core @swc/helpers

Output:

text
added 2 packages in 3s
json
// tsconfig.json
{
  "ts-node": { "swc": true }
}
bash
ts-node ./script.ts

Output:

text
[script] ran (~3× faster than tsc)

SWC mode skips type-checking and uses Rust transpile. Effectively turns ts-node into a slower tsx.

Mocha integration

json
// .mocharc.json
{
  "require": ["ts-node/register"],
  "extensions": ["ts"],
  "spec": "test/**/*.test.ts"
}
bash
npx mocha

Output:

text
  user service
    ✓ creates a user
    ✓ deletes a user
  2 passing (45ms)

Debug under VS Code

json
// .vscode/launch.json
{
  "type": "node",
  "request": "launch",
  "name": "Debug TS file",
  "runtimeExecutable": "node",
  "runtimeArgs": ["-r", "ts-node/register"],
  "args": ["${file}"],
  "skipFiles": ["<node_internals>/**"]
}

Hit F5 with a .ts file open — breakpoints land on TS source thanks to source maps.

Useful flags

FlagWhat it does
--transpile-onlySkip type-checking (faster)
--esmRun in ESM mode
--swcUse SWC transpiler (skips types)
--compiler-options '{"key":"value"}'Override tsconfig compilerOptions inline
--project <path>Use a specific tsconfig.json
--filesForce-include files in files/include even if not imported
--ignore-diagnostics 2304,2307Suppress specific TS error codes
--log-errorPrint errors but continue executing
--prettyColoured diagnostic output
-r <module>Pre-load a CommonJS module (Node's --require shorthand)

Environment variables (all prefixed TS_NODE_):

Env varPurpose
TS_NODE_PROJECTPath to tsconfig.json
TS_NODE_TRANSPILE_ONLY=trueEquivalent to --transpile-only
TS_NODE_IGNORE_DIAGNOSTICSComma-separated error codes to ignore
TS_NODE_COMPILER_OPTIONSJSON string of compilerOptions overrides

Configuration

ts-node reads a ts-node block in tsconfig.json:

json
{
  "compilerOptions": { /* ... */ },
  "ts-node": {
    "transpileOnly": true,
    "esm": true,
    "swc": false,
    "require": ["tsconfig-paths/register"],
    "compilerOptions": {
      "module": "commonjs"
    }
  }
}

The ts-node.compilerOptions override is useful when your main compilerOptions targets ESM but ts-node needs CommonJS (or vice versa) for a particular script.

Common pitfalls

  1. Error [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" in ESM mode. Use ts-node-esm or --esm, not plain ts-node.
  2. TS2307: Cannot find module '@/utils'. Add tsconfig-paths/register to the loader chain.
  3. Slow startup (5+ seconds). Use --transpile-only or --swc. The TS compiler is genuinely slow on cold starts.
  4. emitDecoratorMetadata not working. Only the real TS compiler (the default ts-node mode) emits decorator metadata. --transpile-only and --swc skip it.
  5. Cannot use import statement outside a module. ESM/CJS confusion — add "type": "module" or use ts-node-esm.
  6. Memory ballooning under long-running dev. TS compiler keeps program state in memory. Restart periodically; or move to tsx.

See also