【テクニカル・上級編】 compositeプロジェクトの概念 – TypeScript実践ガイド

巨大なモノリスを解体せよ:TypeScript `composite` プロジェクトによるアーキテクチャの最適化

大規模なフロントエンド開発において、多くのエンジニアが「TypeScriptの型チェックが遅い」という壁に突き当たる。プロジェクトが肥大化し、`tsconfig.json` が数千行のファイル参照で埋め尽くされる頃、エディタは重くなり、型定義の変更が全モジュールへ伝播する際のビルド時間は、コーヒーを淹れて一服しても終わらないほどに悪化する。

これは単なる「遅い」という問題ではない。ビルド時間が長いことは、開発者の集中力を削ぎ、破壊的なリファクタリングを躊躇させ、結果としてコードベースの腐敗を招く。

ここで登場するのが、TypeScriptの `composite` プロジェクト だ。これは単なる設定ファイルの一種ではなく、巨大な型システムを「疎結合なモジュールの集合体」として再構築するための、アーキテクチャ上の解法である。

—

なぜ `composite` なのか:型チェックの「キャッシュ」という革命

通常の単一 `tsconfig.json` では、TypeScriptコンパイラ(`tsc`)は常にプロジェクト全体を解析し直そうとする。たとえ、ごく一部の型定義しか変えていなくても、だ。

`composite: true` を設定したプロジェクトは、「増分ビルド(Incremental Build)」を前提とする。各サブプロジェクトは独立したコンパイル単位となり、コンパイラは出力された `.d.ts` と `tsconfig.tsbuildinfo` を参照することで、変更がないモジュールを再ビルド対象から除外する。

これにより、メモリ消費量は劇的に抑えられ、型解決の計算コストは線形から対数、あるいは定数へと収束していく。

—

現場で使える設計:ディレクトリ構造の最適化

例えば、`apps/web`(フロントエンド)、`packages/ui`(UIライブラリ)、`packages/core`(ロジック)という構成を想定しよう。

1. 基盤となる `packages/core/tsconfig.json`

すべてのプロジェクトは `composite: true` を持ち、出力先(`outDir`)を指定する必要がある。

{
“compilerOptions”: {
“composite”: true, // これが肝。ビルド情報の生成を有効化
“declaration”: true, // 型定義ファイル(.d.ts)の出力を強制
“declarationMap”: true, // ソースコードと型定義をマッピング(IDEのジャンプ用)
“outDir”: “dist”, // ビルド成果物の出力先
“rootDir”: “src”, // ソースコードのルート
“strict”: true
},
“include”: [“src”]
}

2. 参照する側の `apps/web/tsconfig.json`

ここが重要だ。`references` を記述することで、コンパイラは依存関係をグラフとして認識する。

{
“compilerOptions”: {
“composite”: true,
“baseUrl”: “.”,
“paths”: {
“@my-app/core”: [“../../packages/core/dist”] // 出力された型定義を参照する
}
},
“references”: [
{ “path”: “../../packages/core” } // ここで依存関係を明示
]
}

—

避けるべき「非同期の競合」とパフォーマンスの罠

`composite` を導入する際、最も陥りやすい罠が「依存の循環(Circular Dependency)」だ。プロジェクトAがBを、BがAを参照するような設計は、`tsc` の増分ビルドを破壊し、最悪の場合、デッドロックや未定義の型エラーを引き起こす。

また、`paths` の設定と `references` の設定は、論理的に整合性が取れている必要がある。IDEが `paths` で解決する先と、ビルド時に `tsc` が参照する場所がズレると、「型チェックは通るのに、ビルド時にエラーになる」という、最も精神を削るバグに遭遇する。

実践的なアーキテクチャの知見

  • 単方向グラフを徹底する: `packages/ui` は `packages/core` を参照できるが、逆は許さない。このルールを ESLint の `import/no-restricted-paths` などで物理的に遮断するのが、大規模開発の鉄則だ。
  • 共有型定義の分離: 全ての型を一つの `types` パッケージに詰め込むのはやめよう。それは「型定義のモノリス」を作っているに過ぎない。ドメインごとに分割し、必要最小限の依存関係に留めることが、レンダリング負荷(型推論の深さ)を抑える唯一の道だ。

—

結論:コードの「疎結合」は型システムから始まる

`composite` プロジェクトは、単なるビルド最適化のツールではない。それは、あなたが書くコードの「依存関係」を可視化し、強制的に整理させるためのフレームワークだ。

ビルドが速くなれば、あなたはより頻繁にリファクタリングを行うようになる。リファクタリングが増えれば、コードはより堅牢になる。この好循環を回し続けることこそが、フロントエンド・アーキテクトに求められる最大の責務である。

今すぐあなたの巨大な `tsconfig.json` を切り分け、コンパイラの負荷を解放してほしい。型チェックの完了を待つ時間は、もはやコーヒーを飲む時間ではなく、次のアーキテクチャを構想するための贅沢な思索の時間へと変わるはずだ。

コメント

タイトルとURLをコピーしました