やあ。フロントエンドの迷宮へようこそ。
今日取り上げるのは `tsconfig.json` の中でも、特に多くのエンジニアが「なんとなく」設定して沼にハマる `moduleResolution` だ。
「とりあえず `node` にしておけば動くし、よくわかんないから触らない」という君。その判断、今のモダンな開発環境だと少し危険かもしれない。なぜなら、TypeScriptが「どこでモジュールを探すか」というルールは、ビルドツールやランタイムの進化とともに複雑化しているからだ。
現場で「なぜかモジュールが見つからない」「エディタが型エラーを吐き続ける」といった怪現象に直面したとき、ここを理解しているかどうかでトラブルシュートの速度が劇的に変わる。プロとして、腹落ちさせておこう。
—
なぜ `moduleResolution` を意識する必要があるのか
TypeScriptのコンパイラは、コード内に書かれた `import { something } from “./module”` を見たとき、それがファイルシステムのどこにあるのかを解決しなければならない。
この「探し方」のルールを定義するのが `moduleResolution` だ。歴史的な背景と、現代のバンドラー(Vite, Webpack, esbuild等)との兼ね合いで、主に以下の3つが重要になる。
1. `node`
いわゆるNode.jsの解決アルゴリズムを模したものだ。`node_modules` を再帰的に探し、`package.json` の `main` や `types` を見て解決する。TypeScript 4.x 以前のデファクトスタンダードだったが、現代のフロントエンド開発においては「少し古い」と言わざるを得ない。
2. `classic`
TypeScriptの黎明期に使われていた古典的な戦略だ。AMDやSystemJS時代を彷彿とさせる。現代の開発でこれを使う理由は皆無に近い。もし今設定しているなら、何かの間違いだからすぐに見直そう。
3. `bundler`
これこそが、今のモダンなフロントエンド開発の最適解だ。
ViteやWebpackのように、Node.jsのモジュール解決ルールを「いい感じに」拡張してブラウザ向けに変換するツールを使う場合、TypeScriptにその挙動を伝える必要がある。`moduleResolution: “bundler”` を使うことで、TypeScriptは「あ、これ最後はバンドラーがよしなに解決してくれるんだな」と理解し、より柔軟なインポートを許可してくれるようになる。
—
実践:モダンな `tsconfig.json` の設定
現場でトラブルを避けるための、最も推奨される設定がこれだ。
{
“compilerOptions”: {
// 現代のフロントエンド開発におけるベストプラクティス
// Vite, Webpack 5+ 等と組み合わせる際のデフォルト設定
“module”: “esnext”,
“moduleResolution”: “bundler”,
// 以下は bundler を使う際に合わせておくと幸せになれる設定
“esModuleInterop”: true, // CommonJSとESモジュールの相互運用性を高める
“skipLibCheck”: true, // ライブラリの型チェックをスキップしてビルドを高速化
“strict”: true // 当然、有効にしておこう
}
}
—
現場で膝を打つ「あるある」トラブル
なぜ `bundler` が必要なのか?
例えば、`import { Button } from “@/components/Button”` のようなパスエイリアスを使っているとする。
古い `node` 設定だと、TypeScriptは「`@` なんて名前のフォルダは `node_modules` にないぞ!」とパニックを起こすことがある。`bundler` を指定すると、TSコンパイラは「まあ、パスの解決は別の仕組み(バンドラー側の設定)でやるから、TS側ではエラーにせず許可するよ」という寛容な態度を取ってくれるようになるんだ。
ブラウザが裏側でどう処理しているか
忘れてはいけないのは、TypeScriptがコンパイルした後のJavaScriptは、ブラウザが直接実行するコードだということだ。
ブラウザは本来、Node.jsのように `node_modules` を自動的に探索する機能を持っていない。だからこそ、Viteなどのバンドラーが、膨大なインポートの依存関係を解決して、最終的に単一(あるいは複数の最適化された)JavaScriptファイルにまとめている。
`moduleResolution: “bundler”` は、この「TSの型チェック時の挙動」と「実際のバンドラーの挙動」の乖離を埋めるための重要なブリッジなんだ。
—
まとめ:今日からできること
1. プロジェクトを見直す: 今の `tsconfig.json` を開いて `moduleResolution` を確認してほしい。もし `node` や `classic` になっているなら、一度 `bundler` に変更して `npm run build` を試してみよう。
2. エラーを恐れない: 設定を変えた瞬間、型エラーが出るかもしれない。それは「今まで曖昧だった解決ルールが、正しく検知できるようになった」という証拠だ。喜んで修正しよう。
3. 常に最新のドキュメントを: TypeScriptのチームは、`bundler` モードを標準にしようと動いている。公式の [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/module-resolution.html) は、半年に一度は読み返す価値がある。
設定一つでコードの安全性と開発の快適さは大きく変わる。
「なぜそうするのか」を説明できるエンジニアだけが、この複雑なフロントエンドの世界で長く生き残れるんだ。頑張ってくれ。応援しているよ。

コメント