こんにちは。TypeScriptの深淵へようこそ。
あなたが今、型定義の森で迷い込んでいるなら、それはあなたがTypeScriptの「型システム」という強力なエンジンを正しく回そうとしている証拠です。今日は、多くの開発者が一度は頭を抱える「Intersection Types(交差型)によるプロパティの衝突」について、その本質を解き明かしていきましょう。
ここをクリアすれば、あなたは単なる「型を書く人」から、型を「設計する人」へと進化できます。準備はいいですか?
—
1. Intersection Types(&)の正体:ただの「足し算」ではない
まずは基本のおさらいです。`type A = { x: string }` と `type B = { y: number }` があったとき、`type C = A & B` は `{ x: string; y: number }` になりますよね。
しかし、プロパティの型が異なっていたらどうなるでしょうか?
type User = { id: string; status: number };
type Admin = { id: number; status: number };
type SuperUser = User & Admin;
// SuperUserのidはどうなる?
結論から言うと、`SuperUser` の `id` は `string & number` になります。
TypeScriptにおいて、`string` と `number` の両方を満たす値は存在しないため、`id` の型は `never` になります。
これが「型の衝突」の第一形態です。「え、なぜ自動的に解決してくれないの?」と思うかもしれません。しかし、TypeScriptの型は「集合論」に基づいています。交差型(&)は集合の「積(共通部分)」を求めているだけなのです。
—
2. なぜ「衝突」は現場の敵なのか
開発現場では、APIのレスポンス型を拡張したり、複数のインターフェースを合成する際によくこの問題にぶつかります。
「あるライブラリの型定義を書き換えられないけれど、特定のプロパティだけ型を上書きしたい」といったケースで、単純に `&` を使うと、前述の通り `never` が発生して詰んでしまいます。
では、どうすれば「強制的に片方を優先」させられるのでしょうか?
—
3. 解決策:Mapped Types と Conditional Types の魔法
ここで、型システムを自在に操るための「大人の解決策」を伝授します。
「衝突したら、後の型を優先する(上書きする)」ためのユーティリティ型を自作しましょう。
// 魔法のユーティリティ型:Override
type Override
このコードの何が凄いのか?
1. `keyof U`: 上書きしたい型 `U` が持つプロパティ名を抽出します。
2. `Omit
3. `& U`: 排除した場所に、新しい型 `U` を「差し込む」ことで上書きを完了させます。
これで、`never` に悩まされることはありません。
type User = { id: string; status: number; name: string };
type AdminOverride = { id: number }; // idをnumberにしたい
type AdminUser = Override
// 結果: { status: number; name: string; id: number }
// 見事にidだけがnumberに置き換わりました!
—
4. コンパイラの視点:型評価の裏側
なぜ `Omit` を使う必要があるのか。それは、TypeScriptが「宣言の順序」ではなく「型の構造」で型を決定するからです。
コンパイラは `User & Admin` を見たとき、「両方の条件を満たせ」と命令します。しかし、`Omit` を使った `Override` 型は、コンパイラに対して「元の型から特定の枝を切り落とし、そこに新しい枝を接ぎ木せよ」という明確な手順を指示しています。
この違いが、大規模なアプリケーションにおける型の安定性を左右します。
—
5. まとめ:TypeScriptを掌握するために
今日のポイントを整理しましょう。
- `&` は単なる集合の共通部分。プロパティが競合すると `never` が爆誕する。
- 「上書き」したいなら `Omit` で排除してから合成する。
- 型を「値の集まり」としてイメージし、集合論的に考える癖をつける。
TypeScriptの型システムは、パズルのようなものです。最初は難しく感じるかもしれませんが、一度「型はコンパイラへの命令である」という本質を掴んでしまえば、どんなに複雑な外部ライブラリの型定義でも、自分の思い通りにコントロールできるようになります。
「ここが分からない!」という壁にぶつかった時こそ、あなたの成長のチャンスです。ぜひ、この `Override` 型をあなたのプロジェクトの `types.ts` に忍ばせてみてください。
これからも、型と共に良いコードを書いていきましょう。応援しています!