2025年11月11日、Node.js v25.2.0がリリースされ、ついに TypeScriptコードをビルドなしでそのまま実行できる機能 (Type Stripping)が「Stable(安定版)」として正式にサポートされました。
これにより、DenoやBunに続き、主要なJavaScriptランタイムがすべてTypeScriptのネイティブ実行をサポートした状態になりました。 「ts-nodeや独自ローダーをかませて実行する」のが当たり前だった時代から、 「Node単体でそのまま実行できる」時代へと明確な転換点を迎えています。
本記事では、このType Stripping機能がどのような経緯で導入され、技術的に何をしているのか、そして開発体験にどのような影響を与えるのかを整理してみます。
まずは、Node.jsにおけるTypeScriptサポートの流れを、ざっくり時系列で振り返ります。
最初の一歩は、2024年8月リリースの Node.js v22.6.0 でした。
--experimental-strip-types フラグで有効化.ts)を読み込む際に、型注釈を削除して実行する機能が試験提供この段階ではまだ完全なTSサポートではなく、「型注釈を無視して実行できる最低限の仕組み」が提供された状態でした。
--experimental-transform-types の追加
その後の v22.7.0 では、型の削除だけでは対処できないTypeScript構文(後述)に対応するため、
--experimental-transform-types フラグが追加されました。
enum)をJavaScriptに変換する「限定的なトランスパイル」機能ただし、このフラグはあくまで実験的(experimental)であり、デフォルトで有効になることはありません。
その後、Type Stripping自体は徐々に本体へ統合されていきます。
--experimental-~フラグ無しで、.tsファイルの直接実行が可能に2025年中頃のNode v24.3.0前後では、実験的機能の警告が削除され、 ドキュメント上はなお試験的でありながら、実質的には安定して使える段階へと移行しました。
そして2025年11月、Node.js v25.2.0でついに Type Stripping機能が「Stable」指定となりました。
こうして約1年以上の段階的な検証を経て、Node.jsは「TypeScriptを直接扱えるランタイム」としての位置を確立しました。
「TypeScriptがネイティブ実行できる」と聞くと、 「tsc不要?」「全部のTS構文が動く?」といった疑問が湧きます。 ここでは、Type Strippingが具体的に何をしているのかを整理します。
Type Strippingとは、その名の通り TypeScriptの型注釈や型専用の構文を実行時に取り除き、残りを素のJavaScriptとして実行する仕組み です。
Node.jsは .ts ファイルを読み込む際、型に関する部分を
「空白文字に置き換える」 ことで削除します。
(message: string) → 実行時には (message ) にポイントは 「型チェックは一切しない」 ことです。 つまり、これは「コンパイラ」ではなく「高機能なプリプロセッサ」に近いイメージです。
Node.jsは、この型除去処理を自前で1から実装したわけではありません。 内部的には 「Amaro」 と呼ばれるツールを取り込み、 その裏側で 高速なTypeScriptパーサであるSWC を利用しています。
Bloomberg社の「ts-blank-space」と同系統のアプローチで、 「とにかく軽くて邪魔しない」型除去 を目指した設計になっています。
もちろん、TypeScriptのすべての構文が空白に置き換えられるわけではありません。 Node.jsが対応しているのは、 「消しても実行時に問題がない純粋な型情報」 に限られます。
代表的なサポート外(=そのままでは動かせない)構文は次の通りです。
constructor(public id: number) など)
こうした構文は、従来通り tscでコンパイルするか、
または --experimental-transform-types を併用する必要があります。
--experimental-transform-types の位置づけ
--experimental-transform-types は、Type Stripping単体では扱えない構文を
限定的にJSへ変換するオプションです。
Node.jsチームの基本方針は、「デフォルトは軽い型除去のみに留める」 ことであり、 フル機能TSコンパイラになることは目指していません。
「NodeでTSが動くようになった!」と言っても、 プロジェクトに適用する際にはいくつかの落とし穴があります。 ここでは、特に重要なポイントを整理します。
tsc --noEmit などで回す必要がある繰り返しになりますが、Type Strippingは 型チェックをしません。
tsc --noEmit や tsc --build 等で型チェックを回すつまり「ビルドが不要になった」のではなく、 「実行のためのトランスパイルが不要になった」 と理解するのが良いでしょう。
NodeによるType Stripping実行では、tsconfig.jsonは参照されません。
target(ECMAScriptバージョン)設定は無視 → NodeがサポートするES仕様に合わせるpaths / baseUrl などのパスエイリアス設定も効かない
パスエイリアスを使いたい場合は、Nodeの
サブパスインポート("#alias"形式)
など、Node標準の仕組みに寄せていく必要があります。
Nodeは、拡張子と package.json の "type" フィールドの組み合わせで
ES Modules / CommonJS を判定します。
.ts は、対応する .js と同様に扱われる.mts は常にESM、.cts は常にCJS.ts を含めた拡張子が必須
これは「ビルド前提」で import "./foo.js" と書いていた従来のTSプロジェクトと
互換性がありません。
「Nodeで直接実行するTS」は、最初からNodeの解決ルールに沿った書き方にしておく
のが安全です。
import type を正しく使わないと実行時エラーになる
TypeScriptでは、型だけをインポートする場合に import type が使えます。
NodeのType Strippingでは、これを正しく使うことがほぼ必須になります。
import { Foo } from "./types" と書くと、Nodeは実行時にも値として解決しようとするimport type { Foo } と明示する必要あり
TypeScript 5.0以降では verbatimModuleSyntax を有効にすることで、
こうした問題のあるインポートをコンパイル時に検知しやすくなります。
もう1つ重要な仕様として、Nodeは
node_modules 内の TypeScriptファイルを実行しません。
.ts のままnpmに公開することは想定されていないしたがって、 「ライブラリはコンパイルして配布」「アプリ本体の開発体験向上にType Stripping利用」 という役割分担を意識しておく必要があります。
ここからは、「で、実際どれくらい嬉しいの?」という視点でメリットを整理してみます。
最も分かりやすいメリットは、「実行のためのビルドが不要になる」ことです。
といった場面では、トランスパイルの待ち時間がゼロになり、 「保存 → すぐnodeで起動」というサイクルが実現します。
これまではTypeScriptでNodeアプリを開発しようとすると、例えば次のような構成になりがちでした。
tsc でJSへトランスパイルts-node や tsx でランタイム実行nodemon や ts-node-dev でファイル監視&再起動
現在のNodeは --watch オプションも持っているため、
「NodeだけでTypeScriptの編集〜実行〜自動再起動まで完結」
させることができます。
これにより、 「ツールが多すぎて新人にセットアップを説明するのが大変」 といった問題をかなり緩和できるでしょう。
Type Stripping自体は非常に軽量な処理なため、 従来の「トランスパイル → 実行」よりもオーバーヘッドが少ない という利点があります。
Type Strippingは、型を消す以外はほとんど何もしません。
そのため、「実行時はほぼ純粋なJavaScriptの世界」 でありながら、 開発時にはTypeScriptの型安全性も享受できるという、いいとこ取りの感覚で使えます。
実際のところ、この機能に対する開発者の反応はどうだったのでしょうか。
発表当初、SNSやコミュニティでは
「ついにNodeでもトランスパイルなしでTSが動く」といった声が多く見られました。
早期から実サービスに導入した開発者からは、次のような報告も上がっています。
nodemon を卒業できた」tsx や ts-node が不要になり、環境構築がシンプルになった」実際、Type Stripping自体はv22〜24の間に相当こなれており、 v25.2.0でのStable宣言は「中身が大きく変わった」のではなく「十分に枯れたので看板を変えた」 というニュアンスに近い印象です。
一方で、次のような慎重な声もあります。
ただし、そのようなケースでも、
--experimental-transform-types の活用や、部分的な導入(ツール系だけNode直実行にする等)で
折り合いをつけている例も見られます。
最後に、「この先どうなっていきそうか」という視点で少し未来を眺めてみます。
--experimental-transform-types の今後Node.jsチームは、当面は 「デフォルト:型除去のみ」「追加機能:transform-types」 という二段構えを維持すると見られています。
実装者のひとりであるMarco Ippolito氏も、 「あくまで軽量な型除去がデフォルト路線」 であることを明言しており、 Nodeが「TSコンパイラ」に化けることはなさそうです。
JavaScript標準化委員会(TC39)では現在、 「型注釈を言語として許容しつつ、実行時には無視する」 という方向性の提案が議論されています。
これは、TypeScriptやFlowの型構文を「コメント的」に扱うイメージで、 実現すればブラウザを含むすべてのJSエンジンが 「型情報を読み飛ばして実行」できるようになります。
NodeのType Strippingは、ある意味でこの世界観を先取りした実装と言えます。 もし将来標準仕様として「型注釈OK、ただし実行時は無視」という形が採用されれば、 ブラウザでも「TSをそのまま読み込む」世界が現実味を帯びてきます。
--erasableSyntaxOnly など)TypeScript本体や周辺ツールも、NodeのネイティブTS実行を前提とした機能追加を進めています。
--erasableSyntaxOnly オプションが導入こうした動きにより、 「NodeでTSをネイティブ実行する」ことが前提のプロジェクトも 今後増えていきそうです。
Deno・Bun・NodeのすべてがTSネイティブ対応になったことで、 ランタイム選びの軸は、
といった「周辺体験」に移っていきます。
Nodeはnpmエコシステムの中心として、 「既存資産を最大限活かしつつ、TS時代に追随した」 というポジションを強めていくことになるでしょう。
最後に、Node.jsで開発する立場から、 Type Strippingをどう活用していくと良さそうか を簡単にまとめます。
tsc --noEmit などで実施import type・サブパスインポートなど)に沿った形で、
最初から「Type Stripping前提のコーディングスタイル」を採用するのも一案
個人的には、「TypeScriptを使い始めるハードルを大きく下げてくれる機能」だと感じています。
tscやBabelの設定を覚える前に、
「とりあえず node main.ts してみよう」と言えるのは、
学習者にとってもチームにとっても大きなメリットです。
これからNode.jsでTypeScriptを書くときは、 「どこまでをNodeネイティブ実行に任せ、どこからを従来のビルドに任せるか」 を意識して設計していくと、DXと安全性のバランスが取りやすくなるはずです。