大規模プロジェクトの限界を突破せよ:TypeScript Project Referencesで実現する「疎結合」なアーキテクチャ
現場で開発をしていると、ある日突然「型チェックが終わらない」「変更のたびに全ファイルが再コンパイルされる」という悪夢のような体験をすることはないだろうか?
プロジェクトが大きくなればなるほど、`tsconfig.json` が肥大化し、TypeScriptコンパイラ(`tsc`)は巨大な依存関係のグラフを解くために喘ぎ始める。これが「TSが遅い」と感じる原因のほとんどだ。
今日は、そんな泥沼から脱出し、大規模プロジェクトを「疎結合」なモジュールの集合体へと昇華させるProject Referencesについて、現場の知見を詰め込んで解説する。
—
なぜ「Project References」が必要なのか?
TypeScriptの標準的な運用では、1つの `tsconfig.json` が全ファイルを監視する。これは小規模なら楽だが、数千ファイルを超えるとコンパイラは型情報の再計算に膨大な時間を費やす。
Project Referencesの本質は、プロジェクトを「論理的な境界」で分割し、「必要な箇所だけをビルド・キャッシュする」という仕組みだ。`composite: true` を設定することで、各パッケージは独立したビルド単位となり、依存関係にあるプロジェクトの型定義(`.d.ts`)を再利用できる。
ブラウザ側から見れば、これは「モジュールバンドラーの最適化」を助ける布石にもなる。各プロジェクトが `outDir` に出力された型定義を参照するだけで済むため、コンパイラの脳内メモリ消費が劇的に抑えられるんだ。
—
実践:プロジェクト分割の構成例
例えば、`apps/web`(メインアプリ)と `packages/ui`(共通UIライブラリ)に分けたディレクトリ構造を想定してみよう。
root/
├── tsconfig.json # 全体を束ねる設定
├── packages/
│ └── ui/
│ ├── tsconfig.json # UI用の設定
│ └── src/index.ts
└── apps/
└── web/
├── tsconfig.json # Web用の設定
└── src/index.ts
1. 下位モジュール(packages/ui/tsconfig.json)
まずは依存される側の設定だ。`composite` を忘れずにオンにしてくれ。
{
“compilerOptions”: {
“composite”: true, // これが肝。これがないと参照関係が作れない
“declaration”: true, // 型定義ファイル(.d.ts)の出力を必須にする
“outDir”: “./dist”,
“rootDir”: “./src”
},
“include”: [“src”]
}
2. 上位モジュール(apps/web/tsconfig.json)
次に、UIを参照する側の設定。`references` プロパティで依存先を明示する。
{
“compilerOptions”: {
“composite”: true,
“outDir”: “./dist”
},
“references”: [
{ “path”: “../../packages/ui” } // UIプロジェクトへの依存を記述
]
}
3. 全体を束ねるルートの設定(tsconfig.json)
最後に、プロジェクト全体をビルドするための設定だ。ルートの `tsconfig.json` には `files: []` を設定して、それ自体はビルド対象にしないのが定石だ。
{
“files”: [],
“references”: [
{ “path”: “./packages/ui” },
{ “path”: “./apps/web” }
]
}
—
現場で直面する「落とし穴」と解決策
この構成を導入すると、最初は快適だが、現場では以下のポイントでつまづくことが多い。
- `declarationMap` の活用: デバッグ時に重要だ。`declarationMap: true` を設定しておくと、`web` 側から型定義をクリックした際、`dist` の `.d.ts` ではなく、元の `ui` 側の `.ts` ソースコードに直接ジャンプできるようになる。これは開発効率に直結する。
- ビルド順序の自動解決: `tsc –build` コマンドを実行すれば、TypeScriptが `references` を解析し、依存関係に従って正しい順序でビルドしてくれる。これを `package.json` の `scripts` に入れておくだけで、CI/CDの安定感が段違いになる。
- 循環参照の禁止: Project Referencesを導入すると、コンパイラは循環参照を許さない。これは一見不便だが、アーキテクチャの健全性を保つための「強力な強制力」として機能する。設計ミスをビルド時に検知できるのは、大規模開発においては最高の武器になる。
まとめ:なぜ今、これをやるべきか
大規模プロジェクトにおいて、「すべてを1つにまとめる」のは最早怠慢だ。
Project Referencesを導入することは、単なるビルドの高速化ではない。プロジェクト間に明確な「壁」を作ることで、依存関係を整理し、チームでの並行開発を加速させるための「規律」をコードに埋め込む行為なのだ。
最初は `tsconfig` を分割するのが面倒に感じるかもしれない。だが、一度この「疎結合」な心地よさを知れば、もう巨大な1つの `tsconfig.json` に戻ることはできないはずだ。
さあ、明日からの開発で、まずは小さなライブラリを1つ切り出すところから始めてみてほしい。君のコードベースが、もっと軽やかになるはずだ。

コメント