12k
All articles

Node における TypeScript: 実践的なセットアップ

ESM、tsc コンパイル、ネイティブ型ストリッピングを用いた Node.js での TypeScript セットアップと、本番向け API・スクリプト構築の手法を解説する。

OpenReplay Team
OpenReplay Team
Node における TypeScript: 実践的なセットアップ

ブラウザ向けの TypeScript はすでに書いている。今度はサーバーサイドで動かす必要がある—API、ビルドスクリプト、または SSR のために。問題は、ほとんどのセットアップガイドが古く、CommonJS 設定や現代の Node.js に合わないツールを推奨していることです。

このガイドでは、TypeScript Node.js セットアップの 2 つのアプローチを取り上げます:tsc でコンパイルして JavaScript を実行する方法と、Node のネイティブ型除去機能で .ts ファイルを直接実行する方法です。どちらも機能します。それぞれ異なるシナリオに適しています。

重要なポイント

  • 現代的な TypeScript Node.js プロジェクトで ESM をデフォルトで有効にするには、package.json に "type": "module" を設定する
  • 本番デプロイ、公開パッケージ、および enum、namespace、parameter properties を使用するコードには tsc コンパイルを使用する
  • ローカルスクリプト、開発サーバー、クイックプロトタイプには Node のネイティブ型除去機能を使用する
  • 型除去機能使用時のランタイムエラーを避けるため、型のみのインポートには常に import type を使用する
  • Node の型除去機能は型チェックを行わないため、CI では tsc --noEmit を実行する

基本: Node 24 LTS と ESM

この基盤から始めましょう:

{
  "type": "module"
}

これにより ESM がデフォルトで有効になります。インポートは ESM 構文を使用し、Node はそれに応じてモジュールを解決します。

Node 24 は、このセットアップの現在の LTS ベースラインです(こちらからダウンロード可能: https://nodejs.org/en/download)。

アプローチ 1: tsc でコンパイルし、JavaScript を実行

このアプローチはコンパイルと実行を分離します。本番デプロイ、公開パッケージ、または完全な TypeScript 機能サポートが必要な場合に使用してください。

Node 24 向け tsconfig

{
  "compilerOptions": {
    "target": "ES2024",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "skipLibCheck": true,
    "declaration": true,
    "sourceMap": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "lib": ["ES2024"]
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

この設定の重要な項目:

  • module: NodeNextmoduleResolution: NodeNext: Node の実際のモジュール解決動作に一致
  • verbatimModuleSyntax: 型のみのインポートに明示的な import type を要求—ランタイムエラーを避けるために重要(TypeScript ドキュメント参照: https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax)
  • isolatedModules: 単一ファイルトランスパイルツールとの互換性を確保

スクリプト

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch"
  }
}

npm run build を実行してから npm start を実行します。コンパイルされた JavaScript は dist/ に配置されます。

アプローチ 2: Node のネイティブ TypeScript(型除去)

Node は Node 22 でネイティブ TypeScript サポートを導入し、Node 24 LTS で型除去機能として安定化しました。ビルドステップをゼロにしたいスクリプト、ローカルツール、または開発時に使用してください。

公式ドキュメント: https://nodejs.org/api/typescript.html

動作の仕組み

Node 24+ では、直接実行できます:

node src/index.ts

(古い Node バージョンでは実験的フラグが必要でしたが、Node 24 では不要です。)

重要な制限事項

Node のネイティブ TypeScript は型を除去するのみで、型チェックは行いません。エラーを検出するには、CI やエディタで tsc --noEmit を実行する必要があります。

その他の制約:

  • tsconfig.json を無視: Node はコンパイラオプションを読み取りません
  • 明示的なファイル拡張子が必要: ソースファイルが utils.ts であっても import { foo } from './utils.js' と記述します
  • ESM vs CJS ルールを尊重: package.json の type フィールドが重要です
  • node_modules から TypeScript を実行しない: 依存関係はコンパイルされた JavaScript である必要があります
  • 消去可能な構文のみサポート: enum、namespace、parameter properties は --experimental-transform-types を有効にしない限り失敗します

ファイル拡張子の重要性

混在したモジュール形式の場合:

  • .mts ファイル → 常に ESM
  • .cts ファイル → 常に CommonJS
  • .ts ファイル → package.json の type フィールドに従う

ランタイムエラーの回避

型のみのインポートには import type を使用してください:

// 正しい
import type { Request, Response } from 'express'
import express from 'express'

// 間違い - 型除去機能使用時にランタイムで失敗
import { Request, Response } from 'express'

Node が実行時に設定を無視しても、開発中にこれらを検出するために tsconfig で verbatimModuleSyntax を有効にしてください。

どちらのアプローチを使用するか

tsc コンパイルを使用する場合:

  • 本番デプロイ
  • 公開 npm パッケージ
  • enum、namespace、または parameter properties を使用するコード
  • 本番環境でソースマップが必要なプロジェクト

ネイティブ型除去機能を使用する場合:

  • ローカルスクリプトとツール
  • 開発サーバー(--watch と組み合わせる)
  • クイックプロトタイプ
  • SSR 開発ビルド

実践的な開発セットアップ

両方のアプローチを組み合わせます:

{
  "scripts": {
    "dev": "node --watch src/index.ts",
    "typecheck": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

開発では速度のためにネイティブ実行を使用します。CI では typecheck を実行します。本番環境ではコンパイルされた JavaScript をデプロイします。

まとめ

現代的な TypeScript Node.js セットアップは、古いガイドが示唆するよりもシンプルです。ESM を使用し、NodeNext モジュール解決を設定し、コンテキストに基づいて実行戦略を選択してください。ネイティブ型除去機能は開発とスクリプトに適しています。コンパイル出力は本番環境とパッケージに適しています。両方のアプローチは同じソースコードと tsconfig を共有するため、どちらかに固定されることはありません。

よくある質問

ソースファイルが .ts なのに、インポートで .js 拡張子を使用する必要があるのはなぜですか?

Node のモジュール解決では、ESM に明示的なファイル拡張子が必要です。import from ./utils.js と記述すると、ソースファイルが utils.ts であっても、Node は実行時にその正確なパスを探します。型除去機能は型を削除しますがファイル名は変更せず、tsc は .js ファイルを出力するため、ソースで .js 拡張子を使用することで、両方のシナリオでインポートが機能することが保証されます。

Node のネイティブ型除去機能で enum を使用できますか?

デフォルトでは使用できません。enum にはコード変換が必要で、単なる型の削除では不十分です。--experimental-transform-types フラグを有効にすることで enum、namespace、parameter properties をサポートできますが、これにより複雑さが増します。よりシンプルなセットアップの場合は、enum の代替として as const アサーションを使用した const オブジェクトの使用を検討してください。

Node 24 でも ts-node や tsx は必要ですか?

ほとんどのユースケースでは不要です。Node 24 のネイティブ型除去機能は .ts の直接実行を処理します。ts-node や tsx のようなツールは、tsconfig.json サポート、パスエイリアス解決、フラグなしの完全な TypeScript 変換を追加するオプションの便利機能です。セットアップがこれらの機能を必要とする場合にのみ使用してください。

コンパイルされたソースマップなしで Node の TypeScript をデバッグするにはどうすればよいですか?

ネイティブ型除去機能を使用する場合、Node は .ts ファイルを直接実行するため、スタックトレースの行番号はソースと一致します。コンパイルされたコードの場合は、tsconfig.json で sourceMap を有効にすると、Node は自動的に .js.map ファイルを使用してエラーやデバッガセッションで元の TypeScript の場所を表示します。

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.