【テクニカル・上級編】 extendsプロパティによる設定の継承と階層化 – TypeScript実践ガイド

tsconfig.jsonを「管理」するな、設計せよ。継承が生む堅牢なアーキテクチャの極意

多くの現場を見てきたが、`tsconfig.json`を単なる「コンパイラのオプション設定ファイル」と捉えているエンジニアは多い。だが、大規模なWebアプリケーションのフロントエンドにおいて、tsconfigはプロジェクトの思想を規定する静的な型安全の憲法であるべきだ。

特に、設定を細分化し、`extends`を用いて階層化していくプロセスは、型システムを武器にする我々にとって、最も効率的で「負債を生まない」ためのアーキテクチャ設計だと言える。

なぜ設定の継承(extends)が必要なのか

プロジェクトが成長すれば、必ず「厳格な型チェックを強制したいコアライブラリ層」と、「柔軟性が求められるテストやビルドツール層」、そして「レガシーなJSとの混在を許容せざるを得ない移行層」が混在する。

これらを一つの巨大な `tsconfig.json` に押し込むのは、メモリ効率やコンパイル速度、そして何より開発者の精神衛生上、百害あって一利なしだ。`extends`を使いこなせば、「どこまで厳格であるべきか」という粒度をディレクトリ単位で制御できる。

階層構造の設計例

以下のような構成を推奨する。

// tsconfig.base.json: プロジェクト共通の「正義」
{
“compilerOptions”: {
“strict”: true, // あらゆる緩みを許さない。これが我々のスタンダード
“esModuleInterop”: true,
“skipLibCheck”: true, // 巨大なnode_modulesの型チェックを省く。ビルド速度に直結する
“target”: “ES2022”,
“moduleResolution”: “bundler”
}
}

// tsconfig.app.json: アプリケーション層。DOMへのアクセスを許可
{
“extends”: “./tsconfig.base.json”,
“compilerOptions”: {
“lib”: [“DOM”, “DOM.Iterable”, “ESNext”],
“jsx”: “react-jsx”
},
“include”: [“src”]
}

パフォーマンスとメモリ効率を最大化する「除外」の戦略

TypeScriptのコンパイラ(`tsc`)は、起動時にプロジェクト内の全てのファイルを走査する。この際、`tsconfig`の継承関係が適切でないと、必要のないファイルまでAST(抽象構文木)を生成しようとし、メモリを食いつぶし、ビルドのボトルネックとなる。

特に大規模プロジェクトで「型定義が重い」と感じたら、`extends`先で `include` / `exclude` を厳密に分けるのが鉄則だ。

  • 不要なファイルは徹底的に排除せよ:`node_modules` は当然として、テスト用ファイルや外部生成スクリプトは、アプリケーション本体のコンパイル対象から切り離す。
  • プロジェクト参照(Reference)との併用:`extends` は設定の共有だが、`references` はコンパイルの並列化だ。これらを組み合わせることで、TypeScriptのコンパイラをマルチコアで効率的に回せる。

非同期処理と型安全性:競合を未然に防ぐ設定

非同期の競合や、予期せぬ `any` の混入によるバグは、往々にして「tsconfigの甘さ」に起因する。特に `noImplicitAny` や `strictNullChecks` を `extends` で強制しておくことは、コードが非同期関数で溢れかえるモダンなフロントエンドでは、バグを防ぐための最も安上がりな投資だ。

// tsconfig.strict.json: 特にクリティカルなロジック層向け
{
“extends”: “./tsconfig.base.json”,
“compilerOptions”: {
“noImplicitAny”: true,
“strictNullChecks”: true,
“noUncheckedIndexedAccess”: true, // これが重要。配列外アクセスのバグを型で防ぐ
“noImplicitReturns”: true
}
}

特に `noUncheckedIndexedAccess` は、実務において `undefined` へのアクセスによるランタイムエラーを防ぐ最強の防波堤となる。これをベースから適用できない場合でも、重要なモジュールだけこの設定を継承した `tsconfig` を参照するように設計することで、安全性を段階的に高めることが可能だ。

現場で膝を打つための「継承の優先順位」ルール

`extends` を複数使う場合や、上書きを行う際にハマるのが「優先順位」だ。

1. 配列は上書きされる: `compilerOptions.lib` や `types` などは、`extends` された側で再定義すると、親の設定を完全に置き換える。マージされないことに注意が必要だ。
2. 階層が深いほど優先: 複数の `extends` を連鎖させる場合、後方にある設定が先の設定を上書きする。

この仕様を逆手に取り、「ベースは最も緩く、末端のtsconfigほど厳しくする」という逆ピラミッド構造を意識してほしい。

結びに代えて:アーキテクチャは「制約」である

優れたエンジニアは、コードの柔軟性を語る前に「どこに制約を設けるか」を考える。`tsconfig.json` を適切に継承し、階層化することは、チームのメンバーに対して「このディレクトリでは、この品質を維持せよ」という暗黙の規約を型システムという名の強制力に変換する作業だ。

面倒な設定ファイル一つに過ぎないかもしれない。だが、そこを丁寧に設計したプロジェクトは、数年経ってもコードの腐敗が遅い。TypeScriptを単なる言語として使うのではなく、「ビルドという名のエンジニアリングを制御するエンジン」として使いこなしてほしい。

君の `tsconfig` が、次にリリースするプロダクトをより堅牢にすることを願っている。

コメント

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