TypeScript Project References:巨大なコードベースの「死」を回避する唯一の解
フロントエンドの世界で、コードベースが数百万行に達したとき、多くのエンジニアが直面する絶望がある。`tsc` を叩いた瞬間に始まる、永遠にも思える型チェックの待ち時間。そして、IDEが悲鳴を上げ、メモリを食いつぶしてはクラッシュする言語サーバー。
「モジュールを分割すればいい」——それは正しい。だが、ただディレクトリを切るだけでは、TypeScriptのコンパイラは依然として全体を一つの大きな塊として認識し続ける。ここで登場するのが、`composite: true` を軸とした Project References だ。
これは単なるビルド高速化の道具ではない。大規模アーキテクチャにおける「関心の分離」を、コンパイラレベルで強制する強力な制約装置なのだ。
—
なぜ「複合プロジェクト」が不可欠なのか
TypeScriptのコンパイラは、デフォルトでは「全世界を一度に読み込む」という原始的なアプローチをとる。プロジェクトが肥大化すると、AST(抽象構文木)の生成と型チェックのコストが線形、あるいはそれ以上に増大する。
`composite: true` を設定したプロジェクトは、コンパイラに対し以下の規約を課す。
1. 出力の保証: `tsconfig.json` に `declaration: true` が必須となる。これは他プロジェクトへの型定義を「ビルド済みアーティファクト」として提供することを意味する。
2. インクリメンタルビルドの最適化: 依存先が変更されない限り、コンパイラはそれを再コンパイルしない。これにより、変更箇所だけをピンポイントで型チェックするフローが完成する。
3. 名前空間の隔離: 明示的に参照していないパッケージにはアクセスできなくなる。これにより、スパゲッティコードが物理的に書けなくなる。
—
実践:堅牢なアーキテクチャの構築
具体的に見ていこう。例えば、`core`(ロジック層)と `ui`(コンポーネント層)に分かれたプロジェクト構成を想定する。
1. 基盤となる `core` プロジェクト (`packages/core/tsconfig.json`)
{
“compilerOptions”: {
“composite”: true, // これが魔法のスイッチ。プロジェクト単位のビルドを有効にする
“declaration”: true, // 型定義を別ファイルとして書き出す
“declarationMap”: true, // ソースマップで型定義まで遡れるようにする(デバッグの命綱)
“outDir”: “./dist”,
“strict”: true
}
}
2. 依存する `ui` プロジェクト (`packages/ui/tsconfig.json`)
{
“compilerOptions”: {
“composite”: true,
“outDir”: “./dist”
},
“references”: [
// coreへの依存関係を明示。tscはこの情報を元にビルド順序を決定する
{ “path”: “../core” }
]
}
—
現場で遭遇する「罠」と最適化の知見
この機能を導入すると、必ずいくつかの「泥沼」に足を取られる。現場のチーフアーキテクトとして、これだけは覚えておいてほしい。
1. `declarationMap` がなければデバッグは地獄
`composite` を有効にすると、IDEはソースコードではなく `.d.ts` ファイルを読みに行くようになる。もし不具合が発生した際、宣言ファイルへのジャンプができないと、ライブラリの内部動作を追うことが不可能になる。`declarationMap: true` は必須設定だと心得よ。
2. 「循環参照」という名の死神
Project Referencesにおいて、AがBを参照し、BがAを参照するような構成は即座にエラーとなる。これは設計上の敗北だ。このエラーが出たら、「依存の方向性が間違っている」というコンパイラからの強烈な警告だと受け止め、設計を再考すべきだ。
3. メモリ効率とビルドプロセス
`tsc –build` コマンドを使うことで、依存グラフに基づいた最適化ビルドが実行される。大規模チームでは、`tsconfig.json` を共有した状態で CI/CD を回す際、このビルドプロセスのキャッシュ戦略がパフォーマンスの鍵を握る。
—
結論:コードの「物理的な」分離こそが正義
TypeScriptを大規模で運用するとは、型安全性を維持しつつ、コンパイラの負荷を「分割統治」することに他ならない。
`composite: true` を導入することは、単にビルド時間を短縮するだけではない。「どのモジュールがどのモジュールに依存しているか」という依存関係を、コードベースに物理的に焼き付けることだ。
数年後、あなたのプロジェクトがさらに巨大化したとき、このアーキテクチャこそが、新規メンバーがコードの海で溺れるのを防ぎ、かつコンパイラという強力な味方を手元に置き続けるための、たった一つの道標となるだろう。
さあ、今すぐ `tsconfig` を開き、巨大なモノリスを小さな「プロフェッショナルなサブセット」へと解体する準備を始めるのだ。それが、真にスケーラブルなフロントエンドを構築する唯一の道だ。

コメント