2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Node.jsだけでTypeScriptを実行する時代が来た——ts-node・tsx不要の開発環境構築と移行ガイド

2
Last updated at Posted at 2026-03-11

この記事で分かること

  • Node.jsが 追加ツール無し でTypeScriptを実行できるようになった背景と仕組み
  • ts-node・tsx・tscビルドステップが不要になる条件と、その判断基準
  • ゼロからの環境構築手順(5分で完了)
  • 既存プロジェクトからの移行チェックリスト
  • enum が使えない問題の具体的な解決策
  • やってはいけないこと・よくあるハマりポイント

はじめに

TypeScriptでNode.jsの開発をするとき、こんな経験はありませんか?

  • ts-node をインストールしたらバージョン不整合でエラーになった
  • tsxts-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-nodetsx といったツールです。

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-nodetypescript(実行用途として)を削除でき、node_modulesが軽量化されます。

使えない構文を知る:ErasableとNon-erasable

Type Strippingには「型を空白に置き換える」という制約上、使えない構文があります。理解の鍵は「ホワイトスペースで消せるかどうか」です。

使える構文(Erasable)

型注釈、interfacetype、ジェネリクス、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は、その未来を先取りした実装と言えるでしょう。

まとめ

  1. Node.js v22.18.0以降、node file.ts だけでTypeScriptが実行できる — ts-node/tsx/tscのビルドステップは開発時に不要
  2. 仕組みは「型をホワイトスペースに置換するだけ」 — 超高速で、ソースマップも不要
  3. enum/namespace/パラメータプロパティは使えないas constパターンで代替可能
  4. 型チェックはNode.jsの仕事ではないtsc --noEmitをCIに組み込むのがベストプラクティス
  5. importパスに.ts拡張子が必要 — 移行時の最大のハマりポイント

2026年、TypeScript開発の「普通」が変わりつつあります。まだ試していないなら、node hello.ts の一行から始めてみてください。


こちらもよく読まれています

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?