Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
Node.jsComparison

Running TypeScript in Node.js: built-in type stripping vs tsx vs tsc

Node.js now runs .ts files directly by stripping types. Compare built-in type stripping with tsx and a full tsc build: what each supports, what breaks (enums, decorators, paths), and which to pick in 2026.

Cloudsoft Zone

7 min read

Over-the-shoulder view of a developer editing code in a dark-themed code editor on a laptop
Photograph: Tirza van Dijk tirzavandijk / CC0, via Wikimedia Commons
In this article

Running TypeScript on the server used to require a build step or a loader: compile with tsc and run the JavaScript output, or start the app through ts-node or tsx. Node.js now runs many .ts files directly. Type stripping is enabled by default and, in the Node.js 24 documentation, marked stable since 24.12.0.

That raises a practical question for every new project: do you still need a TypeScript build or a third-party runner? The answer depends on which TypeScript features you use, whether you publish packages, and how you deploy. This article compares the three options available in October 2026, explains exactly what built-in type stripping does and does not support, and gives a recommended setup for a new service.

Everything below was checked against the Node.js 24 and 26 documentation and release notes on 7 October 2026, and the examples were run on Node.js 24.

The three ways to run TypeScript on Node.js

Built-in type strippingtsx (or a similar runner)Compile with tsc, run JS
Extra dependency at runtimeNoneYesNone (TypeScript is a dev dependency)
Type checkingNo; run tsc separatelyNo; run tsc separatelyYes, during the build
Enums, namespaces with code, parameter propertiesNot supportedSupportedSupported
Reads tsconfig.json (paths, target)NoYesYes
DecoratorsNot supportedCheck the runner's documentationSupported
Startup costLowLoader transforms each fileNone at runtime
Publishing to npmNot suitableNot suitableStandard approach

The Node.js documentation itself presents the choice in these terms: for full support of all TypeScript syntax and features, including tsconfig.json, use a third-party package such as tsx; for lightweight support, use the built-in type stripping.

Close-up of colourful syntax-highlighted source code filling a computer monitor
Type stripping replaces type annotations with whitespace, so line numbers in stack traces stay correct.

How built-in type stripping works

Node.js replaces type annotations with whitespace and runs the result as JavaScript. Because the code positions do not move, stack traces point to the right lines without source maps. No type checking happens: a file with type errors runs as long as its JavaScript is valid.

The feature arrived in Node.js 22.6.0 behind a flag. It has been enabled by default since 23.6.0 on the 23.x line and since 22.18.0 on the 22.x line, stopped printing an experimental warning in 24.3.0, and was marked stable in 24.12.0. The Node.js 22 documentation still labels it a release candidate.

A small example. Note the .ts extension in the import and the type keyword on the type-only import:

TypeScript
// src/prices.ts
export type Currency = 'USD' | 'INR' | 'EUR';

export interface Price {
  amount: number; // minor units, e.g. cents or paise
  currency: Currency;
}

export function formatPrice(price: Price, locale = 'en-IN'): string {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: price.currency,
  }).format(price.amount / 100);
}
TypeScript
// src/main.ts
import { formatPrice, type Price } from './prices.ts';

const plan: Price = { amount: 1999, currency: 'USD' };
console.log(formatPrice(plan));
Shell
$ node src/main.ts
$19.99

There is no build step and no loader. With "type": "module" in package.json, .ts files follow the same module rules as .js files; .mts files are always ES modules and .cts files are always CommonJS.

What type stripping does not support

Anything that requires generating JavaScript, rather than deleting types, fails. The documentation lists the main cases:

  • enum declarations
  • namespace blocks that contain runtime code (type-only namespaces are fine)
  • parameter properties, such as constructor(private db: Db)
  • import aliases (import x = require(...))
  • decorators, which Node.js will not support until JavaScript itself does
  • .tsx files, and TypeScript inside node_modules

Here is what an enum does:

TypeScript
// src/bad.ts
enum Plan { Free, Pro }
console.log(Plan.Pro);
Shell
$ node src/bad.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

The usual replacement is a constant object plus a derived union type, which type strips cleanly and gives the same autocompletion:

TypeScript
// erasable alternative to an enum
export const Plan = { Free: 0, Pro: 1 } as const;
export type Plan = (typeof Plan)[keyof typeof Plan];

The transform flag is gone in Node.js 26

Node.js 22 and 24 offer --experimental-transform-types to handle enums and similar syntax by generating code. Node.js 26.0.0 removed that flag, as listed among its semver-major changes. If your code depends on it, either rewrite those constructs or use a full TypeScript runner or compiler before you move to Node.js 26.

Other rules that catch people out

  • File extensions are mandatory in imports and require() calls: import './file.ts', not import './file'.
  • Type-only imports need the type keyword. Without it, Node.js treats the import as a value import and fails at runtime. The verbatimModuleSyntax compiler option makes tsc enforce the same rule.
  • tsconfig.json is ignored. paths aliases do not work; use package.json subpath imports (which must start with #) instead.
  • No REPL support; TypeScript syntax is not supported in the REPL, with --check or in inspect.
Conference attendees at a round table with open laptops during a JavaScript event
Choosing between type stripping, tsx and tsc is a team decision about tooling and deployment.

Keep type checking: tsc as a linter

Because Node.js does not check types, you still want tsc in your workflow, in the editor and in CI. The Node.js documentation recommends TypeScript 5.8 or newer with these compiler options when you rely on type stripping:

JSON
{
  "compilerOptions": {
    "noEmit": true,
    "target": "esnext",
    "module": "nodenext",
    "rewriteRelativeImportExtensions": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  }
}

erasableSyntaxOnly makes the compiler reject enums, parameter properties and other constructs that Node.js cannot strip, so you see the problem in the editor rather than at startup. noEmit fits services that only run .ts files; drop it if you also emit JavaScript, for example to publish a package.

A minimal package.json for a service then looks like this:

JSON
{
  "type": "module",
  "scripts": {
    "start": "node src/main.ts",
    "dev": "node --watch src/main.ts",
    "typecheck": "tsc",
    "test": "node --test"
  },
  "devDependencies": {
    "typescript": ">=5.8"
  }
}

Pin the exact TypeScript version you install in your lockfile. node --test picks up *.test.ts files automatically, so tests need no extra setup either.

When to use tsx or a full compile instead

Choose a third-party runner such as tsx when you need TypeScript features that require code generation (enums, decorators used by frameworks, parameter properties) or you depend on tsconfig.json settings such as paths. The Node.js documentation shows two ways to start it:

Shell
npm install --save-dev tsx
npx tsx src/main.ts
# or
node --import=tsx src/main.ts

Choose a full compile with tsc (or another bundler) when you:

  • publish to npm. Node.js refuses to strip types inside node_modules, deliberately discouraging packages that ship only TypeScript. Publish compiled JavaScript plus declaration files.
  • use a framework that relies on decorators and metadata, as several dependency-injection frameworks do.
  • need to target an older runtime or change JavaScript output, which type stripping never does.

Migrating an existing project step by step

If you already run a service through a loader and want to try built-in type stripping, move in small, reversible steps:

  1. Upgrade Node.js first. Use 24.12.0 or newer on the 24.x line, where the feature is stable, or a current 22.x release where it is on by default.
  2. Turn on erasableSyntaxOnly and verbatimModuleSyntax in tsconfig.json and fix the errors tsc reports. Typical fixes are replacing enums with as const objects, turning parameter properties into ordinary fields assigned in the constructor, and adding type to type-only imports.
  3. Add .ts extensions to relative imports. rewriteRelativeImportExtensions lets tsc rewrite them to .js if you also emit JavaScript.
  4. Replace paths aliases with subpath imports in package.json, for example "imports": { "#lib/*": "./src/lib/*" }.
  5. Switch the start command from the loader to plain node src/main.ts in one environment, run your tests and compare behaviour, then remove the loader dependency.

If step 2 produces hundreds of errors, for example because a framework relies on decorators, that is a sign to keep your current runner or build for this project and use type stripping for new scripts and smaller services instead.

A decision guide

Your projectSuggested approach
New API, scripts or CLI tools, no decoratorsBuilt-in type stripping, with tsc --noEmit in CI
Framework that uses decoratorsThe framework's own build (usually tsc)
Existing code with many enums or paths aliasestsx now; migrate gradually to erasable syntax if you want to drop it
npm libraryCompile to JavaScript and declarations
Still on Node.js 20Upgrade first; 20.x is end of life

Summary

  • Node.js runs erasable TypeScript directly; the feature is stable from 24.12.0 and on by default in current 22.x releases.
  • It strips types and does nothing else: no type checking, no tsconfig.json, no enums, no decorators.
  • --experimental-transform-types was removed in Node.js 26, so do not build on it.
  • Keep tsc as a type checker with erasableSyntaxOnly and verbatimModuleSyntax enabled.
  • Use tsx or a compiler when you need non-erasable features or publish packages.

Next step: enable erasableSyntaxOnly in an existing project and see how many errors it reports. That number tells you how close you are to dropping your TypeScript runtime loader.

Frequently asked questions

  • Does Node.js type-check TypeScript files?

    No. It only replaces type syntax with whitespace and runs the JavaScript. Run tsc (for example with noEmit) in your editor and CI to catch type errors.

  • Why does my enum fail with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX?

    Enums need generated JavaScript, which type stripping does not do. Replace the enum with an as const object and a union type, or run the file with a full TypeScript runner or compiler.

  • Can I use tsconfig paths with built-in type stripping?

    No. Node.js ignores tsconfig.json. Use package.json subpath imports, which must start with #, or a runner that reads tsconfig.json.

  • Is --experimental-transform-types still available?

    It exists in Node.js 22 and 24 as an experimental flag, but Node.js 26.0.0 removed it.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Modules: TypeScript (Node.js v24 docs) — Node.js
  2. Modules: TypeScript (Node.js v22 docs) — Node.js
  3. Node.js 26.0.0 (Current) release notes — Node.js, 2026-05-05
  4. Node.js v26 changelog — Node.js
  5. Test runner: running tests from the command line — Node.js

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.