【Node.js】ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX の直し方

エラーを示す端末画面と、修正を表すレンチ・歯車・チェックマークを配したアイソメトリック調のアイキャッチ画像

Node.js 26で消えた–experimental-transform-typesと、ERR_UNSUPPORTED_TYPESCRIPT_SYNTAXで落ちるenumの書き換え方を、v26.8.2での実行結果とともに示します。

目次

この記事が解決する悩み

  • Node.js 26 に上げたら、それまで動いていた .ts が ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX で落ちるようになった
  • 回避策として使っていた --experimental-transform-types が「bad option」で起動すらしない
  • どの構文がダメなのか、どう書き換えればいいのか分からない

対象読者: Node.js から .ts を直接実行している人(ビルドスクリプト、CLI ツール、小さなバッチなど)
前提知識: Node.js 22 以降の型ストリッピング、TypeScript の基本的な構文

結論

Node.js のネイティブ TypeScript 対応は「型を空白に置き換えるだけ」です。JavaScript のコードを新しく生成する必要がある構文は、そもそも実行できません。対象になるのは enum、ランタイムコードを含む namespace、コンストラクタのパラメータプロパティ、import エイリアスの4つですね。

そして Node.js 26(v26.0.0)で --experimental-transform-types が削除されました。以前はこのフラグを付ければ変換までやってくれたのですが、いまは bad option で起動しません。残された道は「構文を書き換える」「tsc で先に変換する」「tsx などに任せる」の3つですね。

いちばん手っ取り早いのは enum を as const オブジェクトに置き換える方法です。

// fixed.ts
const Color = {
  Red: "red",
  Blue: "blue",
} as const;

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

function paint(color: Color) {
  console.log("paint:", color);
}

paint(Color.Red);

実行結果です。

$ node fixed.ts
paint: red

型は typeof で引き継げるので、呼び出し側のコードはそのまま使えます。この書き換えだけで大半のプロジェクトは通りますよ。

ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX とは — どの Node.js バージョンで出るのか

ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX は、Node.js の型ストリッピング(strip-only モード)が「型を消すだけでは実行できない構文」に当たったときに返すエラーコードです。メッセージは次の形で、後半にどの構文が原因かが入ります。

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

バージョンの条件は、2つに分けて考えると整理しやすいですね。

  • エラー自体は Node.js 26 で新しく出たものではない。 型ストリッピングは Node.js 22 以降の機能で、変換が必要な構文を .ts に書いて strip-only モードで実行すれば同じコードが返ります。つまり 26 未満でも起きます。
  • Node.js 26(v26.0.0)で変わったのは、回避フラグのほう。 以前は --experimental-transform-types を付ければ変換までやってくれたので、このエラーを避けられていました。26 でそのフラグが削除されたため bad option: --experimental-transform-types で起動すらしなくなり、構文を書き換える以外に道がなくなりました。

「26 に上げたら急に落ちるようになった」という症状の正体は、エラーが新設されたのではなく、それまで黙って変換してくれていた逃げ道が消えたことです。

エラーの再現手順

まず手元で再現させます。ホスト環境を汚さないように、公式のポータブル配布を一時ディレクトリに展開して使います。

$ python -c "import urllib.request,zipfile;urllib.request.urlretrieve('https://nodejs.org/dist/v26.8.2/node-v26.8.2-win-x64.zip','node.zip');zipfile.ZipFile('node.zip').extractall('.')"
$ ./node-v26.8.2-win-x64/node.exe --version
v26.8.2

落ちる構文を4つ用意します。ES Modules として動かすので、package.json に "type": "module" を入れておきます。

// enum.ts
enum Color {
  Red = "red",
  Blue = "blue",
}

console.log(Color.Red);

// ns.ts
namespace Counter {
  export let count = 0;

  export function inc() {
    count += 1;
  }
}

Counter.inc();
console.log(Counter.count);

// param.ts
class User {
  constructor(private name: string, public age: number) {}

  greet() {
    return `${this.name} (${this.age})`;
  }
}

console.log(new User("taro", 30).greet());

// alias.ts
import path = require("node:path");

console.log(path.basename("/tmp/a.ts"));

順番に実行すると、こうなります。

$ node enum.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

$ node ns.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode

$ node param.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript parameter property is not supported in strip-only mode

$ node alias.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript import equals declaration is not supported in strip-only mode

すべて exit code 1 で落ちます。4つとも同じエラーコードですが、メッセージの後半に原因が出ているので、どの構文が引っかかっているかはその1行で特定できますね。

なぜ「型を消すだけ」だと落ちるのか

Node.js の型ストリッピングは、TypeScript の型まわりの記述を空白に置き換えるだけの仕組みです。型チェックはしませんし、ソースマップも作りません。行番号がずれないのは、行を消さずに空白で埋めているからです。

引っかかるのは、enum やランタイムコード付きの namespace が「型」ではなく「値」を生み出す構文だという点ですね。enum は実行時にオブジェクトを作る必要があります。パラメータプロパティは this.name = name を暗黙で生やします。これを実現するには JavaScript コードの生成が必要で、空白に置き換えるだけの仕組みでは無理があります。

型だけの namespace は消せるので通ります。実際に確認したのがこちらです。

// erasable.ts
namespace TypeOnly {
  export type A = string;
}

const value: TypeOnly.A = "erasable syntax only";
const config = { retries: 3 } satisfies { retries: number };

console.log(value, config.retries);
$ node erasable.ts
erasable syntax only 3

消せる構文かどうかが分かれ目です。satisfies や型注釈、型だけの namespace は問題ありません。

ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX の直し方(構文別)

enum → as const オブジェクト

先ほどの書き換えが基本形です。値を列挙して型を導出するので、分岐の網羅チェックも効きます。

const Status = {
  Pending: "pending",
  Done: "done",
} as const;

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

function label(status: Status) {
  if (status === Status.Pending) {
    return "処理中";
  }

  return "完了";
}

console.log(label(Status.Done));

namespace → ただのオブジェクトかモジュール

値をまとめたいだけなら、オブジェクトか別ファイルのモジュールにすれば十分です。

// counter.ts
let count = 0;

export function inc() {
  count += 1;
}

export function current() {
  return count;
}

パラメータプロパティ → 明示的に代入する

暗黙の代入を自分で書くだけです。行数は増えますが、挙動は同じになります。

class User {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `${this.name} (${this.age})`;
  }
}

console.log(new User("taro", 30).greet());

import equals → ESM の import

import path = require("node:path") は CommonJS 向けの構文です。ES Modules なら普通の import に置き換えます。

import path from "node:path";

console.log(path.basename("/tmp/a.ts"));

型だけの import には type を付ける

これは構文エラーではなく、実行時のエラーとして出ます。型に type を付けないと値の import として扱われ、存在しないエクスポートを探しに行くためです。

// mod.ts
export type Type1 = string;

export const fn = (value: Type1) => value.toUpperCase();

NG と OK を並べます。

$ node typed_import.ts
SyntaxError: The requested module './mod.ts' does not provide an export named 'Type1'

$ node typed_import_ok.ts
TYPE ONLY IMPORT

拡張子なしの import は解決できない

Node.js の ESM と同じルールで、相対 import には拡張子が必須です。パスは実行時の作業ディレクトリで、長いので省略しています。

$ node extless.ts
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'C:\...\helper' imported from C:\...\extless.ts

node_modules 配下の .ts は実行できない

依存パッケージが TypeScript のまま配布されている場合も、型ストリッピングは拒否します。

$ node use_pkg.ts
Error [ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING]: Stripping types is currently unsupported for files under node_modules

パッケージ作者側がビルド済みの JavaScript を配布するか、こちら側でビルドステップを挟むかのどちらかになります。

tsconfig で事前に落とす

実行してから気づくより、型チェックの時点で止めておきましょう。erasableSyntaxOnly を入れておくと、Node.js が実行できない構文を tsc が先に弾いてくれます。

{
  "compilerOptions": {
    "target": "esnext",
    "module": "nodenext",
    "strict": true,
    "noEmit": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "allowImportingTsExtensions": true
  }
}

実際に TypeScript 6.0.2 で回した結果です。

$ npx -p typescript@6.0.2 tsc -p tsconfig.check.json
enum.ts(2,6): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
param.ts(3,15): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
param.ts(3,37): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
typed_import.ts(1,10): error TS1484: 'Type1' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.

実行時に does not provide an export named で落ちる型 import も、TS1484 として事前に捕まえられます。allowImportingTsExtensions を入れておくと、拡張子付き import の TS5097 は出なくなりますよ。CI に1回入れておくと事故が減りますよ。

どうしても書き換えたくないとき

enum を大量に使っていて即時の書き換えが難しい場合は、実行側を替えるのが現実的です。tsx なら enum もパラメータプロパティも変換して実行できます。

$ npx tsx enum.ts
red

もう1つは tsc で JavaScript に変換してから node で実行する方法です。ビルド成果物を配る形になるので、本番運用ではこちらが安全ですね。

$ npx tsc --outDir dist enum.ts
$ node dist/enum.js
red

直ったかどうかを確認する

書き換えたら、同じコマンドをもう一度流して確かめます。この記事で使った確認は次の3つです。

  • その場で実行する。 node fixed.ts のように書き換え後のファイルを直接実行し、SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX] が出ずに期待した出力(paint: red など)が返れば解消です。型だけの namespace を試した node erasable.ts も同じ見方で構いません。
  • tsc で一括チェックする。 erasableSyntaxOnly を入れた tsconfig.check.json を npx -p typescript@6.0.2 tsc -p tsconfig.check.json で回し、TS1294 / TS1484 が消えていることを確認します。実行する前に落ちる箇所を洗い出せるので、CI に入れるならこちらが本命ですね。
  • フラグで逃げていないかを見る。 --experimental-transform-types を足して直ったつもりになっていないか、起動オプションを確認してください。26 では bad option で落ちるので、そもそも起動しません。

「実行は通るのに別のエラーが出る」場合は、原因が構文ではなくモジュール解決側に移っています。そのときは ERR_MODULE_NOT_FOUND(拡張子なしの import)や ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(node_modules 配下の .ts)を、本文の該当箇所と照合してください。

つまずきポイント

引っかかったのはフラグまわりと、エラーの出方です。

  • --experimental-transform-types はもう存在しない。 付けると bad option: --experimental-transform-types で即終了します。起動オプションの話なので、スタックトレースは出ません。
  • --no-strip-types にすると別のエラーに変わる。 TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" になり、エラーの種類が変わっただけで解決はしません。
  • --experimental-strip-types は今も通る。 ただしストリップしかしないので、enum では同じ ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX のままです。フラグを足せば直る話ではありません。
  • Node.js は tsconfig.json を読みません。 paths や旧構文へのダウングレードは効かないので、実行時の解決は別途考える必要があります。エイリアスの解決は # 始まりのサブパス import に寄せる必要があります。
  • エラーの型が一定ではない。 構文の問題は SyntaxError、モジュール解決の問題は Error、拡張子の問題は TypeError で出ます。エラーの種類で判断しようとすると迷います。
  • .tsx はサポート外。 JSX を含むファイルは対象外なので、そちらは別のツールに任せることになります。

【広告】XServerドメインならドメイン取得も運用もお得。

実測メモ: erasableSyntaxOnly を入れて TypeScript 6.0.2 で型チェックしたところ、enum とパラメータプロパティは TS1294、型だけの import は TS1484 で落ちました。同じ tsconfig に allowImportingTsExtensions を足すと、拡張子付き import の TS5097 は出なくなります。逆に import equals を使っているファイルを対象に含めると、TS1294 に加えてTS2591(Cannot find name 'node:path'、@types/node 未導入のため)も一緒に出ました。エラーの件数だけを見て判断せず、メッセージの種類まで確認しておきましょう。

まとめ

Node.js 26 のエラーは、型ストリッピングが「消せる構文だけ」を対象にしていることと、--experimental-transform-types が削除されたことの合わせ技です。2026年9月時点で、対応は3択に整理できます。

構文 Node.js 26 の挙動 書き換え先
enum ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX as const オブジェクト + typeof
namespace(値あり) ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX オブジェクト / 別モジュール
パラメータプロパティ ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX 明示的な this 代入
import equals ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX ESM の import
型だけの import 実行時エラー type キーワードを付ける
拡張子なし import ERR_MODULE_NOT_FOUND .ts を明記
node_modules 内の .ts ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING 配布側でビルドする

いきなり全ファイルを書き換えるのが難しければ、まず tsconfig.json に erasableSyntaxOnly を入れて、対象ファイルを洗い出すのがおすすめです。落ちる箇所が一覧で出るので、作業量が見えます。そのうえで CI に tsc を挟めば、同じエラーで本番が止まることはなくなりますよ。

検証環境

OS Windows 11 (build 10.0.26200)
CPU AMD Ryzen 9 PRO 8945HS w/ Radeon 780M Graphics
メモリ 28GB
言語/ツール Node.js 26.8.2(公式のポータブル配布を一時ディレクトリに展開) / TypeScript 6.0.2(npx経由で型チェックのみ) / tsx 4.23.13(npx経由)
使用コマンド node enum.ts(再現)/ node fixed.ts(書き換え後)/ npx -p typescript@6.0.2 tsc -p tsconfig.check.json
測定日 2026-09-17

上記の環境で実際に実行して確認した結果です。環境が異なる場合は挙動が変わることがあります。

Node.js 26.8.2 に型ストリッピング非対応の構文を実行させた実測ログ。4構文の再現、消えたフラグの bad option、tsc の TS1294 / TS1484 検出までを1枚にまとめたもの
Node.js 26.8.2 に型ストリッピング非対応の構文を実行させた実測ログ。4構文の再現、消えたフラグの bad option、tsc の TS1294 / TS1484 検出までを1枚にまとめたもの

参考

本文の記述は以下の一次情報を確認して書いています。

あわせて読みたい

【広告】このサイトはConoHa WINGで運営しています。安定した高速サーバーで快適にブログを書けています。いつもありがとう!!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次