intersection型は「AもBも両方持つ」型
まず一言でいうと、
intersection型(インターセクション型、&)は 「AかつB」=「Aの性質もBの性質も同時に持つ型」 です。
対比でいうとこうなります。
- union型(
|):A または B(どちらか一方でよい) - intersection型(
&):A かつ B(両方満たしていないとダメ)
イメージとしては、「型Aが持つプロパティ」+「型Bが持つプロパティ」を合体した“全部乗せ”の型を作る感じです。
type HasName = {
name: string;
};
type HasAge = {
age: number;
};
type Person = HasName & HasAge;
const p: Person = {
name: "Taro",
age: 20,
};
TypeScriptPerson は HasName と HasAge の両方を満たす必要があります。
片方だけ欠けているとエラーになります。
const bad: Person = {
name: "Taro", // age がないのでエラー
};
TypeScript「intersection型=“AもBも両方の条件を満たすもの”」という感覚をまず持ってください。
union型との違いをしっかりイメージで分ける
union型は「どれか1つでいい」、intersection型は「全部必要」
同じ例で、union型と intersection型を並べてみます。
type HasName = { name: string };
type HasAge = { age: number };
type NameOrAge = HasName | HasAge; // union
type NameAndAge = HasName & HasAge; // intersection
TypeScriptNameOrAge(union)の値は、次のどちらでも OK です。
const a: NameOrAge = { name: "Taro" }; // OK
const b: NameOrAge = { age: 20 }; // OK
TypeScriptしかし NameAndAge(intersection)の場合、両方必須になります。
const c: NameAndAge = { name: "Taro", age: 20 }; // OK
const d: NameAndAge = { name: "Taro" }; // エラー
const e: NameAndAge = { age: 20 }; // エラー
TypeScriptつまり、
union型 → 「この型か、この型か、どれか1つならOK」
intersection型 → 「この型もこの型も、ぜんぶ満たしているものだけOK」
という関係です。
ここが混ざると一気に分かりづらくなるので、
頭の中で「or(または)」と「and(かつ)」をはっきり分けることがめちゃくちゃ大事です。
intersection型のよくある使い方(オブジェクトを“足し算”する)
共通の情報+役割ごとの情報を合成する
複数の型を「足し合わせて」新しい型を作るのに、intersection型はとても向いています。
type BaseUser = {
id: number;
name: string;
};
type WithEmail = {
email: string;
};
type WithAdminFlag = {
isAdmin: boolean;
};
type AdminUser = BaseUser & WithEmail & WithAdminFlag;
TypeScriptAdminUser は、
BaseUserのidとnameWithEmailのemailWithAdminFlagのisAdmin
全てを持つ型になります。
const admin: AdminUser = {
id: 1,
name: "Taro",
email: "taro@example.com",
isAdmin: true,
};
TypeScriptここでの intersection型は、
「小さな役割ごとの型(BaseUser / WithEmail / WithAdminFlag)を、“組み合わせて使える部品”として定義しておく」
ための道具になっています。
「このAPIでは email も isAdmin も要るから、両方乗せた AdminUser がほしい」
みたいなときに、& でガチャっと合体させるイメージです。
関数の引数で intersection型を使う感覚
「この関数を呼ぶには、Aの情報もBの情報も全部必要」
関数の引数に intersection型を使うと、「この条件もあの条件も満たしている値しか渡させない」ことができます。
type HasId = { id: number };
type HasToken = { token: string };
type AuthInfo = HasId & HasToken;
function fetchUser(info: AuthInfo) {
console.log(info.id, info.token);
}
TypeScriptこの関数を呼ぶ側は、id だけ持っていてもダメですし、token だけでもダメです。
fetchUser({ id: 1, token: "abc" }); // OK
fetchUser({ id: 1 }); // エラー
fetchUser({ token: "abc" }); // エラー
TypeScriptここで intersection型が表現しているのは、
「この処理を行うには、この情報もこの情報も“全部そろっている必要がある”」
というビジネスルールそのものです。
逆に、「id か token のどちらかあればいい」なら、それは intersection ではなく union です。
type WeakAuthInfo = HasId | HasToken;
// どちらか片方だけでもOKになる
TypeScriptintersection型を使うときは、
「ここは“どちらかあればいい”のか、“両方なきゃダメ”なのか?」
を意識して選び分けることがとても重要です。
intersection型のやや難しいポイント(型が「詰め合わせ」にならないケース)
プリミティブ同士の intersection は“共通部分”になる(基本は避けてOK)
オブジェクト同士の intersection は「プロパティの合体」になって分かりやすいですが、
プリミティブ型同士だと、少し違う動きになります。
例えば:
type A = string & number;
TypeScriptこれは「string かつ number」=「同時に string であり number でもある型」ですが、そんな値は存在しません。
実際には never(存在しない型)に近いものになります。
いきなりここを深追いする必要はありません。
初心者のうちは、
「intersection は“オブジェクト型”を足し合わせるために使うもの」
と割り切ってしまって大丈夫です。
もう少し進んだ段階で、「条件付き型」や「mapped 型」と組み合わせるときに、
「intersection は“型の共通部分を取る”方向にも働く」という側面が見えてきます。
intersection型を設計するときに考えてほしいこと
「この型とこの型を“同時に満たすもの”って、世界の中で何を意味する?」
intersection型を使う前に、必ず自分にこう問いかけてみてください。
この2つの型(A と B)を同時に満たす値って、現実世界では何に相当する?
それは本当に一つの “役割” として意味がある?
例えば、
BaseUser:基本的なユーザー情報
WithEmail:メールアドレスを持つ
WithAdminFlag:管理者フラグを持つ
これらを全部持つ AdminUser には、「メールアドレスを持っていて、管理者権限も持つ“特別なユーザー”」という役割があります。
逆に、意味のない組み合わせを作ってしまうと、
「この intersection 型って、何者なの?」
「どんなときにこの型の値が存在するの?」
というモヤモヤが残り、設計が濁ります。
intersection型は、ただの「足し算ツール」ではなく、
「複数の性質を同時に持つものに名前を与えるための道具」です。
だからこそ作るときは、
「この性質とこの性質を両方持っている存在に、名前をつける意味があるか?」
を一度立ち止まって考えてみてください。
それが、型が“生きた設計”になるかどうかの分かれ目になります。
まとめ:intersection型を自分の言葉で言うと
intersection型(&)は、ざっくり言えばこういうものです。
- 「A または B」ではなく、「A かつ B」
- 複数の型のプロパティやルールを全部まとめて背負わせる
- 「この性質も、この性質も、両方持っている存在」に名前をつけるための型
オブジェクトで使うときは、
共通の情報を持つ Base な型
役割ごとの“追加情報”を持つ小さな型
を組み合わせて、
「この役割の人は、これとこれとこれを全部持っている」
という形で表現できます。
コードを書いていて、
「この人(値)は、A としての顔も持っていて、B としての顔も持っているな」
「この処理を実行するには、この情報もこの情報も全部必要だな」
と感じたときが、intersection型を検討するタイミングです。
その感覚を大事にしながら & を使い始めると、
型が単なる「制約」から、「ドメイン(現実世界)のルールをそのまま写したもの」に変わっていきます。
