【入門編】Type Aliasにおける「Intersection Types」のプロパティ衝突と解決策 – TypeScript コア・型システムの基礎解析バイブル

こんにちは。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 = Omit & U;

このコードの何が凄いのか?

1. `keyof U`: 上書きしたい型 `U` が持つプロパティ名を抽出します。
2. `Omit`: 元の型 `T` から、`U` に存在するプロパティだけを「排除」します。
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` に忍ばせてみてください。

これからも、型と共に良いコードを書いていきましょう。応援しています!

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