TypeScript | 基礎文法:関数の基礎 – in演算子による型ガード

TypeScript
スポンサーリンク

「in演算子による型ガード」は何をしてくれるのか

in 演算子による型ガードは、
「このオブジェクトは、このプロパティを持っているか?」を調べることで、型を絞り込む仕組みです。

人間の感覚だと、こうです。

permissions というプロパティを持っているなら、きっとこれは管理者(Admin)だよね?」

その「感覚」を TypeScript に伝えるのが、

"permissions" in obj
TypeScript

という書き方です。
この条件の中では、「objpermissions を持つ型だ」と TypeScript が理解してくれるので、
obj.permissions に安全にアクセスできるようになります。

ここから、実際のコードでじっくり掘り下げていきます。


union型の「どの型か」をプロパティの有無で判定する

まずは典型例:User と Admin の union

次のような2種類の型を考えます。

type User = {
  name: string;
};

type Admin = {
  name: string;
  permissions: string[];
};

type Person = User | Admin;
TypeScript

PersonUserAdmin のどちらかです。
Admin だけが、permissions プロパティを持っています。

ここで、Person を引数にとって「管理者かどうかで処理を分ける」関数を書いてみます。

function printPerson(person: Person) {
  if ("permissions" in person) {
    console.log("管理者です:", person.permissions);
  } else {
    console.log("一般ユーザーです:", person.name);
  }
}
TypeScript

ここで起きていることを丁寧に分解します。

最初、person の型は User | Admin(どっちか分からない)です。
if ("permissions" in person) と書くと、

permissions というプロパティを持っている型はどれ?」
Admin だけ
→ じゃあこの if の中では personAdmin として扱っていい

と TypeScript が判断します。

だから if の中で person.permissions を使ってもエラーになりません。
逆に else の中では「permissions を持っていない方」、つまり User に絞られます。

このように、「どのプロパティを持っているか」を手がかりに、union 型の一部だけに絞る
それが in 演算子による型ガードです。


typeof と in の役割の違いを感覚で掴む

typeof は「ざっくりした種類」、in は「持っている機能」

typeof は、string, number, boolean, object, function などの
「ざっくりした種類」を見るものでした。

いっぽう in は、
「このオブジェクトは、この“能力”(プロパティ)を持っているか?」
をチェックします。

たとえば、さっきの例で言うと、

permissions という権限リストを持っているなら、管理者」
「それを持っていないなら、普通のユーザー」

という“役割の違い”をプロパティの有無で判定しているわけです。

オブジェクトの型を設計するときに、

「この役割を持つものだけが、このプロパティを持っている」

というルールを作っておくと、in がとても使いやすくなります。


もう少し複雑な例:複数パターンをプロパティで見分ける

APIレスポンスの成功/失敗をプロパティで分ける

よくあるのが、「成功レスポンス」と「エラーレスポンス」の型を分けるケースです。

type SuccessResponse = {
  ok: true;
  data: {
    id: number;
    name: string;
  };
};

type ErrorResponse = {
  ok: false;
  error: string;
};

type ApiResponse = SuccessResponse | ErrorResponse;
TypeScript

ここで ApiResponse を受け取って処理する関数を書きます。

function handleResponse(res: ApiResponse) {
  if ("data" in res) {
    // ここでは res は SuccessResponse 型に絞られる
    console.log("ユーザー名:", res.data.name);
  } else {
    // ここでは res は ErrorResponse 型
    console.error("エラー:", res.error);
  }
}
TypeScript

"data" in res でチェックしているのは、
data プロパティを持っているレスポンスだけが成功レスポンスである」という設計です。

その設計を前提にして、

"data" in res → SuccessResponse
それ以外 → ErrorResponse

というふうに、TypeScript に型を絞り込ませています。

大事なのは、
「どのプロパティを、どの型だけに持たせるか」という設計が、in 型ガードの土台になっている
ということです。


in演算子の「型としての読み方」を言語化する

“prop” in obj が TypeScript にとって意味すること

"permissions" in person と書いたとき、
TypeScript はざっくりこんなことを考えています。

「union に含まれる型のうち、“permissions プロパティを持っている型”はどれ?」
→ その型に絞り込んでいい

逆に、else 側では「permissions を持たない方」に絞られます。

つまり、
"prop" in obj は「obj が prop を持つ型かどうかを判定する“型のフィルター”」
だと読めます。

これを自分の言葉で説明できるようになると、in 型ガードを自然に使えるようになります。


判別可能なunionとの違いと、組み合わせ方

kind / type プロパティとの比較

「判別可能な union」では、よく kindtype というプロパティを使って型を分けます。

type Circle = {
  kind: "circle";
  radius: number;
};

type Square = {
  kind: "square";
  size: number;
};

type Shape = Circle | Square;
TypeScript

これを使った型ガードは、たとえばこんな感じです。

function area(shape: Shape) {
  if (shape.kind === "circle") {
    // Circle
    return Math.PI * shape.radius ** 2;
  } else {
    // Square
    return shape.size ** 2;
  }
}
TypeScript

ここでは === によるリテラルチェックで型を絞っています。

同じことを in でやるなら、

function area(shape: Shape) {
  if ("radius" in shape) {
    // Circle(radius を持つのは Circle だけ)
    return Math.PI * shape.radius ** 2;
  } else {
    // Square
    return shape.size ** 2;
  }
}
TypeScript

という書き方もできます。

どちらを選ぶかは設計次第ですが、感覚としては:

kind / type などの「ラベル」で分ける → 判別可能 union の王道パターン
特定の“能力”(プロパティ)の有無で分ける → in 演算子の型ガードが自然に合う

どちらも「型を絞る」という意味では同じです。
ただ、in のほうが「実際の機能の違い」に近いことが多いです。


in演算子による型ガードを設計に活かす視点

「このプロパティを持っているものだけができること」は何か?

in 型ガードを上手く使うには、こう自分に問いかけてみると良いです。

このドメイン(問題の世界)で、
「この能力(プロパティ)を持っているオブジェクトだけができること」は何か?

例えば「管理者だけが権限リストを持っている」「成功レスポンスだけが data を持っている」など。

その答えを型に落とすと、

管理者だけが permissions を持つ
成功レスポンスだけが data を持つ

といった形になります。

その上で、関数の中で

if ("permissions" in person) {
  // ここでは person は「権限を持つ」という能力を持っている型
}
TypeScript

と書くと、「このブロックの中で何をしているのか」が非常に分かりやすくなります。

「プロパティの有無」で役割を分けると、型ガードが自然になる

オブジェクト設計の段階で、

「このロールの人だけがこの情報を持っている」
「この状態だけがこのフィールドを持っている」

というルールを意識しておくと、
in の型ガードが自然にフィットします。

逆に、「どの型も似たようなプロパティを適当に持っている」状態だと、
in 型ガードは使いづらくなり、条件分岐もぐちゃっとしがちです。

「プロパティの有無で役割を切り分ける設計」と「in による型ガード」はセットで考えると強い
この感覚は、少しずつ育てていけると良いと思います。


まとめ:in演算子による型ガードの“本質”

ここまでをぎゅっとまとめると、in 型ガードの本質はこうです。

あるオブジェクトが
「このプロパティを持っているかどうか」
を調べることで、そのオブジェクトの型(役割)を絞り込む仕組み

具体的には、

"prop" in obj という条件が true のブロックでは、
prop を持つ型」だけが残る

という動きをします。

だからこそ、設計側では、

「このプロパティを持つものだけが、特定の役割や能力を持っている」

というルールを意識して型を作ると、
in 型ガードが「ドメインのルールをそのまま表現する文法」になってくれます。

コードを書いていて、

「このオブジェクトが X か Y かは、“このプロパティを持っているかどうか”で見分けているな」

と感じたら、そこが in による型ガードを使うポイントです。
その瞬間、TypeScript はあなたの「頭の中の見分け方」をちゃんと理解し、
条件分岐ごとに“型の世界”をきれいに整理してくれる相棒になってくれます。

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