node app.ts が動くようになったので TypeScript をそのまま渡したら、enum のところで止まった、というエラーです。
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
at parseTypeScript (node:internal/modules/typescript:68:40)
コピペ検索用の表記ゆれです。最後の語が違うだけで、原因はどれも同じです。
TypeScript enum is not supported in strip-only modeTypeScript namespace declaration is not supported in strip-only modeTypeScript parameter property is not supported in strip-only modeTypeScript import equals declaration is not supported in strip-only mode
Node は TypeScript を「消す」だけで、「変換」はしません。 enum が拒否されるのも、何を書けば動くのかも、この 1 点から決まります。
Node の TypeScript サポートは「型を消す」だけ
Node が .ts を実行できるのは、型注釈を削り落として JavaScript として動かしているからです。この動作を Node は strip-only mode(消すだけのモード)と呼びます。エラーメッセージにもその名前が出ています。
型注釈は消せます。消したあとに残る JavaScript は、元と同じ意味です。
function label(s: string): string { // : string を消すだけ
return s;
}
enum は消せません。 enum は型ではなく、実行時にオブジェクトを作る構文だからです。
enum Status {
Active = "active",
}
これを消してしまうと、Status.Active を参照するコードが動きません。かといって Node は、enum を等価な JavaScript に組み立て直すこと(変換)はしません。だから受け取りを拒否します。
同じ理由で通らない構文
enum だけが特別なわけではありません。実行時のコードを生む TypeScript 構文は、どれも同じエラーになります。
| 構文 | 実行時に何を生むか | strip-only |
|---|---|---|
enum | オブジェクト | 通らない |
const enum | 参照先の値(インライン化する変換が要る) | 通らない |
値を含む namespace(export const など) | 即時実行関数 | 通らない |
パラメータプロパティ constructor(private x: string) | 代入文 | 通らない |
import fs = require("node:fs") | require 呼び出し | 通らない |
型だけの namespace / declare namespace | 何も生まない(消せる) | 通る |
type / interface / as / satisfies / declare | 何も生まない(消せる) | 通る |
消せるものは通り、生むものは通りません。
namespace が表の両側に出てくるのは、この線のせいです。namespace N { export const x = 1 } は実行時にオブジェクトを組み立てるので通りませんが、namespace N { export type A = string } のように型しか入っていない namespace は消せるので通ります(declare namespace も同じです)。「namespace は使えない」ではなく、「値を書いた namespace は使えない」です。
直し方 1:消せる構文で書き直す(推奨)
enum の使い勝手は、as const のオブジェクトと、その値のユニオン型で再現できます。フラグは要りません。
// Before(ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX になる)
enum Status {
Active = "active",
Archived = "archived",
}
function label(s: Status): string {
return `status=${s}`;
}
console.log(label(Status.Active));
// After(既定の Node でそのまま動く)
const Status = {
Active: "active",
Archived: "archived",
} as const;
type Status = (typeof Status)[keyof typeof Status];
function label(s: Status): string {
return `status=${s}`;
}
console.log(label(Status.Active));
Status.Active という呼び出し方も、Status を型として使うことも、そのまま残ります。値は素の JavaScript のオブジェクト、型は型注釈なので、どちらも消せる側に収まります。
直し方 2:--experimental-transform-types(Node 26 で削除)
enum を残したまま動かすフラグが、かつてありました。
node --experimental-transform-types app.ts
変換モードを明示的に有効にする指定で、これなら enum は等価な JavaScript に組み立て直されて動きます。
ただし、このフラグは Node 26 で削除されました。
$ node --experimental-transform-types app.ts
node: bad option: --experimental-transform-types
使える版は限られます。
| Node | --experimental-transform-types |
|---|---|
| 22.7.0 〜 25.x | 使える |
| 26 以降 | 存在しない(bad option で起動しない) |
代わりのフラグは用意されていません。Node 26 以降で残るのは、直し方 1(消せる構文へ書き直す)か、tsc などでビルドしてから .js を実行する従来の方式の 2 つだけになります。
名前の似たフラグに注意
node --experimental-strip-types app.ts # enum は通りません
--experimental-strip-types は消すだけのモードを有効にするフラグで、enum は通りません。付けてもまったく同じエラーが出ます。「実験的フラグを付ければ動くはず」と考えてこちらを付けると、何も変わらないまま行き詰まります。
紛らわしいのは、このフラグの意味が版によって変わることです。
- 22.6.0 〜 22.17.x / 23.0 〜 23.5.x:型ストリッピングがまだ既定ではないので、このフラグが
.tsを実行するために必要です。付けなければERR_UNKNOWN_FILE_EXTENSIONで止まります。 - 22.18.0 以降 / 23.6.0 以降 / 24 / 26:型ストリッピングが既定なので、このフラグは既定と同じモードを明示するだけです。付けても何も変わりません。
Node 26 でも --experimental-strip-types は受け付けられます(bad option にはなりません)。削除されたのは transform のほうだけです。
型チェックは通ります
tsc は enum を何とも思いません。 型チェックは通り、エラーは実行時にだけ出ます。
$ npx tsc --noEmit app.ts
(何も出ない。エラー無し)
enum は正しい TypeScript です。型チェッカから見て、問題は 1 つもありません。CI の型検査は緑のまま通り、node app.ts の実行だけが落ちます。
このズレは、TypeScript 5.8 以降の erasableSyntaxOnly で埋められます。**「消せない構文を書いたら型検査で落とす」**という指定です。
// tsconfig.json
{
"compilerOptions": {
"erasableSyntaxOnly": true
}
}
これを入れると、enum は型検査の時点で弾かれます。
app.ts(1,6): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
Node で実行する前に、エディタと CI が教えてくれるようになります。node で .ts を直接動かす方針なら、入れておく価値があります。
切り分け
--experimental-strip-typesを付けたのに何も変わらない → 型ストリッピングが既定の版(22.18.0 以降)では、そのフラグは既定と同じモードを明示するだけです。enumは通りません。書き直してください(Node 26 では--experimental-transform-typesも使えません)。- エラーが
ERR_UNKNOWN_FILE_EXTENSION: ".ts"→ こちらは.tsをそもそも実行できていません。Node の版が古いか、型ストリッピングが有効になっていない状態です。ERR_UNKNOWN_FILE_EXTENSION: “.ts” の直し方 を参照してください。このエラー(ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX)は、.tsの実行自体には成功していて、中の構文で止まっているという一段先の状態です。 enumを消したのに、まだ同じエラーが出る → メッセージの最後の語を読んでください。namespace declarationやparameter propertyなら、別の場所で同じ制限に当たっています。上の表で該当する構文を探してください。- エラーが
ERR_UNSUPPORTED_TYPESCRIPT_SYNTAXではなく、ただのSyntaxError: Invalid or unexpected tokenで、^が@の行を指している → それは decorator です。この記事の変換フラグでは直りません(decorator は変換されない別の境界です)。SyntaxError: Invalid or unexpected token の直し方(decorator) を参照してください。 - 依存ライブラリの
.tsで落ちている → それは別のエラーです(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING)。node_modulesの中の.tsは構文を見る前に弾かれるので、そもそもこのエラーにはなりません。フラグでは外せません。 node_modules の中の .ts は実行されない を参照してください。 tsxでは動いていた →tsxは変換まで行うのでenumも動きます。Node の組み込みサポートは変換しないので、同じコードでも動く/動かないが分かれます。
検証環境
node:24.18.0-alpine、ネットワーク無し- 再現:
enumを含む.tsをnode reproduce/app.tsで実行し、ERR_UNSUPPORTED_TYPESCRIPT_SYNTAXが出て終了コード 1 - 修正:同じ処理を
as constのオブジェクトとユニオン型で書き直すだけで終了コード 0・シグネチャ消滅。フラグは付けていません。出力(status=active)も再現側と同じです - 通らない構文(
enum/const enum/ 値を含むnamespace/ パラメータプロパティ /import x = require())と、通る構文(型だけのnamespace/declare namespace/type/interface/as/satisfies/declare)は、それぞれ個別のファイルをnode:24.18.0-alpineで実行して確認しました - 「
--experimental-transform-typesでenumが動く(22.18.0 / 24.18.0 / 25.9.0)」「Node 26.5.0 ではbad optionになり、フラグ自体が存在しない」「--experimental-strip-typesは Node 26 でも受け付けられるがenumは通らない」「Node 22.18.0 / 23.11.1 / 24.18.0 / 26.5.0 のいずれでもenumは通らない」「Node 22.6.0 では型注釈だけの.tsすら既定では実行できない」も、それぞれ実行して確認しています - 「
tsc --noEmitはenumを通す」「--erasableSyntaxOnlyを付けるとerror TS1294で落ちる」は、[email protected]をインストールして確認しました
tsconfig.json 経由での erasableSyntaxOnly の設定は、コマンドラインの指定と機構が同じ(型検査時に消せない構文を弾く)ことを根拠にした対処であり、tsconfig.json からの再現は通していません。ts-node / tsx / esbuild での挙動も確認していません。