「in演算子による型ガード」は何をしてくれるのか
in 演算子による型ガードは、
「このオブジェクトは、このプロパティを持っているか?」を調べることで、型を絞り込む仕組みです。
人間の感覚だと、こうです。
「permissions というプロパティを持っているなら、きっとこれは管理者(Admin)だよね?」
その「感覚」を TypeScript に伝えるのが、
"permissions" in obj
TypeScriptという書き方です。
この条件の中では、「obj は permissions を持つ型だ」と TypeScript が理解してくれるので、obj.permissions に安全にアクセスできるようになります。
ここから、実際のコードでじっくり掘り下げていきます。
union型の「どの型か」をプロパティの有無で判定する
まずは典型例:User と Admin の union
次のような2種類の型を考えます。
type User = {
name: string;
};
type Admin = {
name: string;
permissions: string[];
};
type Person = User | Admin;
TypeScriptPerson は User か Admin のどちらかです。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 の中では person を Admin として扱っていい
と 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」では、よく kind や type というプロパティを使って型を分けます。
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 はあなたの「頭の中の見分け方」をちゃんと理解し、
条件分岐ごとに“型の世界”をきれいに整理してくれる相棒になってくれます。
