こんにちは。TypeScriptという言語の深淵に挑む皆さん、ようこそ。
日々コードを書いていると、「`interface`と`type`、どっちを使えばいいの?」という問いに一度はぶつかりますよね。結論から言うと、小さなプロジェクトではどちらを使っても大きな差はありません。しかし、数万行を超えるエンタープライズ級のコードベースに足を踏み入れた瞬間、その「型定義の書き方」がコンパイル時間に牙を剥くことになります。
今日は、TypeScriptのコンパイラが裏側でどう型を処理しているのか、その「重み」を意識した設計戦略についてお話しします。
—
1. そもそも「継承」と「交差型」は何が違うのか?
まずは基本をおさらいしましょう。
interfaceの「継承 (extends)」
`interface`は、オブジェクトの「設計図」を拡張するために作られています。
interface User {
id: string;
}
// 継承:既存の設計図を元に新しい設計図を作る
interface Admin extends User {
role: string;
}
typeの「交差型 (Intersection Types)」
`type`は、型同士を「混ぜ合わせる」という数学的な集合演算に近い概念です。
type User = { id: string };
type Admin = User & { role: string }; // 交差型:両方の性質を併せ持つ型
一見同じに見えますよね? しかし、コンパイラにとっては「脳の使いどころ」が全く異なります。
—
2. なぜ大規模開発で「交差型」が重くなるのか?
ここが本日の核心です。TypeScriptのコンパイラ(`tsc`)は、型を評価する際に「名前」を追跡します。
interfaceの利点:名前による解決
`interface`が継承されるとき、コンパイラは「Aという名前の型をベースにBを追加した新しい型C」という明確なフラット構造を保持しようとします。これにより、コンパイラは型の整合性をチェックする際、効率的なキャッシュを利用できます。
交差型の罠:遅延評価と型展開
一方で、`User & Admin & Moderator & …` と複雑な交差型を組むと、コンパイラは「これらの型をすべてマージして、最終的なプロパティの集合体は何か?」という計算を毎回行う必要があります。
数千行のプロジェクトで、この「型演算」が深層まで入り込むとどうなるか。
1. コンパイラが型の「答え」を出すために、再帰的に型を展開しようとする。
2. 複雑な交差型が絡み合うと、キャッシュが効かず、コンパイル時間が指数関数的に増大する。
3. エディタ(VSCodeのLanguage Server)の動作が重くなり、型定義ホバーがいつまで経っても表示されない……。
これが、「型を書きすぎたせいで開発体験が最悪になる」という現象の正体です。
—
3. パフォーマンスを最適化する「型設計」の黄金律
大規模開発で「コンパイル爆速」を維持するために、次の戦略を意識してください。
戦略A:interfaceを優先して使用する
特別な理由がない限り、オブジェクトの定義には `interface` を使いましょう。`interface` は継承関係が明確であるため、コンパイラにとって非常に「読みやすい」形式です。
戦略B:複雑な型演算は「名前付き」で切り出す
交差型を直接書くのは避け、一度新しい型に格納してください。
// 悪い例:直接複雑な交差型を書くと、tscが毎回計算に苦しむ
const processData = (data: User & Admin & LoggedIn & Verified) => { … }
// 良い例:名前をつけて評価を確定させる(コンパイラがキャッシュしやすくなる)
interface ProcessableUser extends User, Admin, LoggedIn, Verified {}
const processData = (data: ProcessableUser) => { … }
戦略C:循環参照を避ける
型定義が `A -> B -> A` のように循環すると、コンパイラは無限ループを防ぐために計算を打ち切ります(これが有名な「Type instantiation is excessively deep」エラーです)。継承を繰り返す際は、依存関係が単方向(DAG:有向非巡回グラフ)になるよう注意してください。
—
まとめ:型は「構造」ではなく「表現」である
TypeScriptの型システムは、単なるバリデーションツールではなく、コンパイラとの対話です。
- `interface` は、コンパイラに「このオブジェクトはこういう構造ですよ」と教える宣言的な指示書です。
- `type` (交差型) は、コンパイラに「この型とこの型を計算して新しい型を作ってくれ」と頼む命令的な計算式です。
小さなアプリなら命令(計算)してもすぐ終わりますが、巨大なアプリケーションで毎日膨大な計算を繰り返せば、コンパイラも疲弊してしまいますよね。
「継承」と「交差型」を適切に使い分けること。それは、あなたのコードを読みやすくするだけでなく、コンパイラの負荷を減らし、開発効率を最大化する「アーキテクトとしての作法」です。
ここを意識できるようになれば、あなたはもうTypeScriptの初心者ではありません。一段上の景色へようこそ。また次のステップでお会いしましょう!