← Home

Node Runs Your TypeScript Now — Except for Five Things

Type stripping is on by default and it does not compile, it deletes. Five constructs have no runtime-free equivalent to delete, and each one stops your process at startup.

A stack of translucent sheets being pulled away to reveal a clean white page, with two or three sheets jammed at the top.

You delete your build step, run node server.ts, and it works. Then you deploy and the process exits before it prints anything.

Stripping is not compiling

This is the whole model, and it explains all five failures. Node does not compile your TypeScript — it deletes the annotations and runs what is left. Types, interfaces, satisfies, as: all of them vanish and the JavaScript underneath is byte-identical. That is why it needs no source maps and cannot change your program’s behaviour.

So the rule is not “which features are supported”. It is: does this construct need Node to emit runtime code? If it does, there is nothing to delete, and it fails.

Check a file

Erasable syntax checker

4 constructs Node cannot strip

What Node actually says

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

The erasable rewrite

Not erasable

enum Colour {
  Red,
  Green,
}

Erasable

const Colour = {
  Red: 0,
  Green: 1,
} as const;

type Colour = (typeof Colour)[keyof typeof Colour];

This is a regular-expression check, not a TypeScript parser — a real parser is hundreds of kilobytes and this page has a 220 KB budget. It can produce false positives. Error messages were recorded from Node v25.9.0 on 2026-08-06.

That is a regular-expression check, not a TypeScript parser — a real parser is hundreds of kilobytes and this page has a 220 KB JavaScript budget. It can produce false positives, which is the honest trade: a checklist that admits it is a checklist is safer than a tool that pretends to be a compiler.

Every error message in it was recorded by actually running the code on Node v25.9.0, not copied from documentation. You will paste those strings into a search box, so they have to be verbatim.

The five, and why each one needs emitted code

enum produces an object at runtime — you can index it both ways. Deleting the declaration would leave the call sites referring to nothing.

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:
TypeScript enum is not supported in strip-only mode

const enum fails identically, which surprises people who assume it is inlined.

Namespaces with runtime values are the same story — namespace Config { export let retries = 3 } has to become an object. A namespace containing only types is fine and Node accepts it; that is the one exception, and it is easy to miss because the error message just says “namespace declaration”.

Constructor parameter properties are pure sugar for an assignment Node would have to write for you:

constructor(private readonly name: string) {}
// has to emit: this.name = name

import fs = require('node:fs') needs rewriting into a real import.

Decorators are the odd one out. They are not unsupported TypeScript — they are not JavaScript yet, a TC39 Stage 3 proposal. Node has nothing to strip, so it fails earlier and differently, with a plain SyntaxError: Invalid or unexpected token and no ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX code to search for.

What to do

  1. Turn on erasableSyntaxOnly in tsconfig. It makes the compiler reject these five at type-check time, which is where you want to find out — not at process start in production.
  2. Rewrite the first four. They all have erasable equivalents, shown in the component above, and every one of them is also plainer JavaScript than what it replaces.
  3. Decorators are the real fork. They are the one thing with no erasable rewrite. If your codebase depends on them, you are keeping a build step, and that is a legitimate answer — just make it a decision rather than a discovery.

Common follow-ups

Why can Node not just compile these?

Because stripping and compiling are different jobs. Stripping removes annotations and leaves byte-identical JavaScript underneath, which needs no source maps and cannot change behaviour. The moment it emits code — an enum object, a field assignment — it is a compiler, with all the versioning and correctness surface that implies.

Are type-only namespaces affected?

No. A namespace containing only types is erasable and Node accepts it. Only a namespace that declares runtime values fails. Verified on Node v25.9.0.

What replaces enum?

A frozen object plus a derived type. `const Colour = { Red: 0, Green: 1 } as const` with `type Colour = (typeof Colour)[keyof typeof Colour]` gives you the same call sites, and unlike an enum it is plain JavaScript that any tool understands.

What about decorators?

There is no erasable rewrite. Decorators are a TC39 Stage 3 proposal, not JavaScript, so there is nothing for Node to strip. If you need them you still need a build step — tsc, SWC or esbuild.