TypeScript | 基礎文法:Union・基本型操作 – intersection型の基本

TypeScript
スポンサーリンク

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,
};
TypeScript

PersonHasNameHasAge の両方を満たす必要があります。
片方だけ欠けているとエラーになります。

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
TypeScript

NameOrAge(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;
TypeScript

AdminUser は、

  • BaseUseridname
  • WithEmailemail
  • WithAdminFlagisAdmin

全てを持つ型になります。

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になる
TypeScript

intersection型を使うときは、
「ここは“どちらかあればいい”のか、“両方なきゃダメ”なのか?」
を意識して選び分けることがとても重要です。


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型を検討するタイミングです。

その感覚を大事にしながら & を使い始めると、
型が単なる「制約」から、「ドメイン(現実世界)のルールをそのまま写したもの」に変わっていきます。

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