この記事で分かること
- Node.jsが 追加ツール無し でTypeScriptを実行できるようになった背景と仕組み
- ts-node・tsx・tscビルドステップが不要になる条件と、その判断基準
- ゼロからの環境構築手順(5分で完了)
- 既存プロジェクトからの移行チェックリスト
-
enumが使えない問題の具体的な解決策 - やってはいけないこと・よくあるハマりポイント
はじめに
TypeScriptでNode.jsの開発をするとき、こんな経験はありませんか?
-
ts-nodeをインストールしたらバージョン不整合でエラーになった -
tsxとts-nodeの違いがよく分からず、どちらを使うか迷った -
tsconfig.jsonの設定で半日潰れた - プロジェクトごとに 微妙に違うビルド設定を管理するのが苦痛
2026年現在、これらの悩みに対する答えが出ました。Node.js本体にTypeScript実行機能が標準搭載されたのです。
# これだけでTypeScriptが動く。本当にこれだけ。
node hello.ts
ts-node も tsx も tsc のビルドも要りません。この記事では、この機能の仕組みから実践的な移行手順までを、初心者にも分かるように解説します。
前提知識
この記事を読むにあたり、以下の基礎を簡単に押さえておきましょう。
TypeScriptとは
JavaScriptに 型注釈(かたちゅうしゃく) を加えた言語です。型注釈とは「この変数には文字列しか入れません」といった宣言のことで、コードのバグを事前に見つけやすくなります。
// 型注釈の例:name は string(文字列)型しか受け付けない
function greet(name: string): string {
return `Hello, ${name}!`;
}
なぜこれまで「変換」が必要だったのか
Node.jsが理解できるのはJavaScriptだけです。TypeScriptのコードは「型の部分を取り除いてJavaScriptに変換する」ステップが必要でした。その変換を担っていたのが ts-node や tsx といったツールです。
Type Strippingとは何か
Node.jsに搭載された新機能の名前は Type Stripping(タイプ・ストリッピング) です。日本語にすると「型剥がし」——型注釈をコードから剥がして除去する、という意味です。
身近な例えで説明すると、食品の包装フィルムを剥がしてそのまま調理するイメージです。食材(JavaScript)の上に貼ってある保護フィルム(型注釈)をペリッと剥がすだけで、中身はそのまま使えます。
仕組み:型をホワイトスペースに置換する
Type Strippingの処理は驚くほどシンプルです。
// 元のTypeScript
function greet(name: string): string {
return `Hello, ${name}!`;
}
// Type Stripping後(Node.jsが実際に実行するコード)
function greet(name ) {
return `Hello, ${name}!`;
}
型注釈の部分が 空白(ホワイトスペース) に置き換わっただけです。これにより、行番号がずれないという大きなメリットが生まれます。エラーが起きたとき、スタックトレースの行番号がそのまま.tsファイルの行番号と一致するため、ソースマップが不要になります。
内部的には Amaro というライブラリ(Rustで書かれた超高速トランスパイラ SWC のWebAssembly版)が処理を行っています。
重要な設計思想:型チェックはしない
Type Strippingは 型を除去するだけで、型チェックは一切行いません。string型の引数にnumberを渡してもNode.jsは何も言わずに実行します。
function greet(name: string): string {
return `Hello, ${name}!`;
}
// 型エラーだが、Node.jsは何も言わず実行する
greet(42); // → "Hello, 42!"
型チェックは引き続き tsc --noEmit やIDEに任せる——これがNode.jsの設計方針です。Node.jsは「TypeScriptのランタイム」ではなく「JavaScriptのランタイム」であり続ける、という明確なスタンスです。
バージョンごとの進化
Type Strippingは段階的に安定化してきました。
| Node.jsバージョン | 時期 | 内容 |
|---|---|---|
| v22.6.0 | 2024年8月 |
--experimental-strip-types フラグで初登場 |
| v22.7.0 | 2024年8月 |
--experimental-transform-types 追加(enum等に対応) |
| v23.6.0 | 2025年1月 | Type Strippingが デフォルト有効化(Current版) |
| v22.18.0 | 2025年7月 | LTS版でもデフォルト有効化(実務で使える起点) |
| v24.3.0 | 2025年 | Experimental警告が消去 |
| v25.2.0 | 2025年11月 | stable(安定版)に昇格 |
実務で使う場合は v22.18.0以上(LTSでデフォルト有効)が最低ラインです。新規プロジェクトなら v25系を推奨します。
環境構築:ゼロから5分で動かす
ステップ1:Node.jsのバージョン確認
node -v
# v22.18.0 以上であることを確認
v22.18.0未満の場合は、nvm等でアップデートしてください。
ステップ2:TypeScriptファイルを作って実行する
// hello.ts
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `こんにちは、${user.name}さん(${user.age}歳)!`;
}
const user: User = { name: "太郎", age: 30 };
console.log(greet(user));
$ node hello.ts
こんにちは、太郎さん(30歳)!
以上です。 ts-nodeのインストールもtsconfig.jsonの作成も不要です。
ステップ3(推奨):tsconfig.jsonで型チェック環境を整える
Type Strippingは型チェックをしないため、開発品質を保つにはtsconfig.jsonの設定が推奨されます。
{
"compilerOptions": {
"target": "es2024",
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"erasableSyntaxOnly": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
erasableSyntaxOnly: true を設定すると、Type Strippingで使えない構文(後述)を書いた時点でコンパイラが警告してくれます。
ステップ4(推奨):package.jsonのスクリプト設定
{
"scripts": {
"dev": "node --watch src/index.ts",
"start": "node src/index.ts",
"typecheck": "tsc --noEmit"
}
}
node --watch(Node.js v18.11.0以降で利用可能)を組み合わせれば、ファイル変更時に自動で再起動されます。nodemonも不要です。
ts-nodeとの比較:何が変わるのか
| 比較軸 | ts-node | Node.js Type Stripping |
|---|---|---|
| 起動速度 | 約120ms | 約45ms |
| インストール | npm install ts-node typescript |
不要(ビルトイン) |
| 設定ファイル | tsconfig.json必須 | 不要(型チェック用に推奨) |
| enum対応 | 対応 |
--experimental-transform-types で対応 |
| 型チェック | オプションで実行 | 行わない |
| ソースマップ | 生成する | 不要(行番号が保持される) |
| メモリ使用量 | 大(トランスパイラをメモリにロード) | 小 |
最大の違いは 依存関係が消える ことです。package.jsonからts-nodeとtypescript(実行用途として)を削除でき、node_modulesが軽量化されます。
使えない構文を知る:ErasableとNon-erasable
Type Strippingには「型を空白に置き換える」という制約上、使えない構文があります。理解の鍵は「ホワイトスペースで消せるかどうか」です。
使える構文(Erasable)
型注釈、interface、type、ジェネリクス、asキャスト、import type——これらは「消しても実行に影響しない」ため問題なく使えます。
使えない構文(Non-erasable)
| 構文 | 理由 | 代替策 |
|---|---|---|
enum |
実行時にオブジェクトを生成する必要がある |
as const パターン |
namespace(実行コードあり) |
実行時にコードを含む | モジュール分割 |
| クラスのパラメータプロパティ | コンストラクタ内のコード生成が必要 | 明示的なプロパティ宣言 |
JSX(.tsxファイル) |
JSXは型注釈ではない | バンドラー(Vite等)を併用 |
enumの代替:as const パターン
enumが使えない点はNode.jsネイティブTS最大の制約ですが、as constパターンでほぼ完全に代替できます。
// ❌ Node.js Type Strippingでは動かない
// enum Status {
// Active = 'ACTIVE',
// Inactive = 'INACTIVE',
// }
// ✅ as const パターンで代替
const Status = {
Active: 'ACTIVE',
Inactive: 'INACTIVE',
} as const;
// 型を自動生成(enumと同等の型安全性)
type Status = (typeof Status)[keyof typeof Status];
// → 'ACTIVE' | 'INACTIVE'
function printStatus(status: Status): void {
console.log(`現在のステータス: ${status}`);
}
printStatus(Status.Active); // ✅ OK
printStatus('ACTIVE'); // ✅ OK
// printStatus('UNKNOWN'); // ❌ 型エラー(tscが検出)
パラメータプロパティの代替
// ❌ 動かない
// class User {
// constructor(private name: string, private age: number) {}
// }
// ✅ 明示的に書く
class User {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
移行チェックリスト:既存プロジェクトをステップバイステップで
既存のts-nodeプロジェクトからの移行は、以下のチェックリストに沿って進めてください。
最大のハマりポイント:importの書き方
ts-nodeからの移行で最も躓くのが importパス です。
// ❌ ts-node時代の書き方(動かない)
import { helper } from './utils';
import { MyType } from './types';
// ✅ Node.jsネイティブTS対応の書き方
import { helper } from './utils.ts'; // 拡張子.tsが必須
import type { MyType } from './types.ts'; // 型はimport typeが必須
import type を使わずに型をインポートすると、Node.jsはそれを値のインポートとして扱い、ランタイムエラーになります。tsconfig.jsonの verbatimModuleSyntax: true を有効にすれば、この問題をコンパイル時に検出できます。
よくあるエラーと対処法
| エラーメッセージ | 原因 | 対処法 |
|---|---|---|
SyntaxError: TypeScript enum is not supported in strip-only mode |
enumを使っている |
as constオブジェクトで代替 |
ERR_MODULE_NOT_FOUND |
importパスに.ts拡張子がない |
import ... from './file.ts'に修正 |
SyntaxError: Unexpected token ':' |
Node.jsのバージョンが古い | v22.18.0以上にアップデート |
TypeError: ... is not a function |
型のみのimportでimport typeを使っていない |
import typeを使う |
ExperimentalWarning: Type Stripping is an experimental feature |
v24.3.0未満の警告 |
node --disable-warning=ExperimentalWarningで抑制 |
ERR_UNSUPPORTED_DIR_IMPORT |
ディレクトリインポートしている | ファイルパスを明示的に指定 |
実践例:Express APIサーバー
実務に近い例として、ExpressでAPIサーバーを構築してみます。
// src/server.ts
import express from 'express';
import type { Request, Response } from 'express';
interface Todo {
id: number;
title: string;
completed: boolean;
}
const app = express();
app.use(express.json());
const todos: Todo[] = [];
let nextId = 1;
app.get('/todos', (_req: Request, res: Response) => {
res.json(todos);
});
app.post('/todos', (req: Request, res: Response) => {
const todo: Todo = {
id: nextId++,
title: req.body.title,
completed: false,
};
todos.push(todo);
res.status(201).json(todo);
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
# ホットリロード付きで開発サーバーを起動
$ node --watch src/server.ts
Server running on http://localhost:3000
ts-node時代の「あの数秒の起動待ち」がゼロになります。ファイルを保存した瞬間にサーバーが再起動される体験は、一度味わうと戻れません。
判断ガイド:いつ使うべきか/使わないべきか
向いているケース
- 新規プロジェクト: 最初からType Stripping前提で設計できる
- CLIツール・スクリプト: 依存を最小限にしたい用途に最適
-
APIサーバー(Express / Fastify等):
node --watchとの組み合わせで快適な開発
向いていないケース
-
フロントエンド開発: JSX(
.tsx)は対象外。Vite等のバンドラーを使う -
ライブラリ公開:
.d.tsの生成やJSへのトランスパイルが必要な場合はtscが必要 - enumに強く依存した大規模プロジェクト: 移行コストが大きい場合は段階的に
今後の展望:TC39 Type Annotationsプロポーザル
Node.jsでのType Strippingは、より大きな流れの一部です。**TC39(JavaScriptの標準化団体)**では「Types as Comments」というプロポーザルが進行中です。これが標準化されれば、ブラウザでも型注釈付きのJavaScript(≒TypeScript)が直接実行できる未来がやってきます。
Node.jsのType Strippingは、その未来を先取りした実装と言えるでしょう。
まとめ
-
Node.js v22.18.0以降、
node file.tsだけでTypeScriptが実行できる — ts-node/tsx/tscのビルドステップは開発時に不要 - 仕組みは「型をホワイトスペースに置換するだけ」 — 超高速で、ソースマップも不要
-
enum/namespace/パラメータプロパティは使えない —
as constパターンで代替可能 -
型チェックはNode.jsの仕事ではない —
tsc --noEmitをCIに組み込むのがベストプラクティス -
importパスに
.ts拡張子が必要 — 移行時の最大のハマりポイント
2026年、TypeScript開発の「普通」が変わりつつあります。まだ試していないなら、node hello.ts の一行から始めてみてください。