TypeScriptの世界へようこそ!チーフアーキテクトの私です。
TypeScriptを書き始めたばかりの頃って、型エラーの赤い波線にビクビクしてしまいますよね。「動くはずなのに、なんでTypeScriptくんは怒るんだ…!」と、夜中に天井を見上げた経験のある人も多いはず。大丈夫、みんな最初はそこを通ります。
今回は、そんなTypeScriptの「構造的部分型」という少しお堅い仕組みと仲良くなるための、とっても便利で優しい魔法「`in`演算子による型ガード」についてお話ししますね。
難しい専門用語はなるべく置いておいて、身近な例えからゆっくり紐解いていきましょう。
—
1. カバンの中身、何が入っている?(型の世界のお話)
TypeScriptの大きな特徴に「構造的部分型(こうぞう的ぶぶんがた)」というものがあります。難しそうに聞こえますが、要するに「そのオブジェクトが、中身としてどんなプロパティ(目印)を持っているか」で型を判断する仕組みのことです。
例えば、あなたが街でお買い物をしているとします。目の前に2つの「袋」があります。
1. 「猫ちゃん専用の袋」(「鳴き声」が入っている)
2. 「ワンちゃん専用の袋」(「お散歩が好き」が入っている)
この時、中身を見ずに「これは猫ちゃん用の袋だ!」と決めつけるのは危険ですよね。ちゃんと袋の中を覗いて、「おっ、猫ちゃんの鳴き声が入っているから、これは猫ちゃん専用の袋だな」と確認(ガード)する必要があります。
TypeScriptの世界でも全く同じことが起きるんです。
—
2. `any`や`unknown`から身を守る、優しい型ガード
プログラムを書いていると、「このデータ、ネコかもしれないし、イヌかもしれない……」という曖昧な状況に直面します。そんな時、TypeScriptに「このデータはどっちなの?」と正しく教えてあげるのが型ガードです。
その中でも `in` 演算子は、「このオブジェクトの中に、指定したプロパティ(目印)が含まれているか?」をサクッと調べてくれる、めちゃくちゃ頼れる相棒なんです。
百聞は一見に如かず。実際にコードを見てみましょう!
ほっこり動物園のコード例
// 猫ちゃんの型定義
type Cat = {
name: string;
meow: () => void; // 「ニャーと鳴く」というプロパティ
};
// ワンちゃんの型定義
type Dog = {
name: string;
bark: () => void; // 「ワンと吠える」というプロパティ
};
// 猫か犬、どっちが来るか分からない関数
function handleAnimal(animal: Cat | Dog) {
// ここで「animalの中に ‘meow’ というプロパティがあるか?」をチェック!
if (“meow” in animal) {
// TypeScriptくん「おっ、’meow’があるってことは、この子は確実にCatだな!」と安心する
console.log(`${animal.name}ちゃんにお魚をあげます`);
animal.meow(); // 安全に猫のメソッドを呼び出せる!
} else {
// こっちは自動的に Dog だと確定する
console.log(`${animal.name}くんに骨のおもちゃをあげます`);
animal.bark(); // 安全に犬のメソッドを呼び出せる!
}
}
どうでしょう? `if (“meow” in animal)` と書くだけで、TypeScriptが「あ、それならこっちの型だね」と賢く判断(絞り込み)してくれていますよね。これが `in` 演算子による型ガードのパワーです。
—
3. つまずきやすいポイントと「大丈夫だよ」のフォロー
初心者の頃、この `in` 演算子を使おうとして、よくこんな壁にぶつかります。
> Q. プリ型(stringやnumberなど)をチェックしようとしてエラーになったんだけど……?
> A. `in` 演算子は、オブジェクトのプロパティの有無を調べるためのものです!
例えば、変数そのものが `string` か `number` かを調べたいときは、`in` 演算子ではなく `typeof` を使います。
- オブジェクトのプロパティを調べたいとき = `in` 演算子 (`”property” in obj`)
- JavaScriptの基本の型(string, number, booleanなど)を調べたいとき = `typeof` 演算子 (`typeof value === “string”`)
使い分けのイメージとしては、こんな感じです。
- 「お財布の中に、小銭入れ(特定のプロパティ)はあるかな?」と探すときは `in`
- 「この手元にあるものは、硬貨(プリミティブ型)かな、それともお札かな?」と素材を確かめるときは `typeof`
最初はごちゃ混ぜになってしまっても全然大丈夫です。エラーが出たら、「あ、オブジェクトの目印を探すときは `in` だったっけな」と思い出してあげてください。
—
4. 実務でのリアルな使い所
Web制作やフロントエンド開発の現場では、外部のAPIからJSONデータ(よく分からないフワッとしたデータ)を受け取ることが本当によくあります。
「サーバーからエラー情報が返ってきたのか、それとも成功データが返ってきたのか分からない!」という時、この `in` 演算子が大活躍します。
type SuccessResponse = {
data: string;
};
type ErrorResponse = {
errorCode: number;
errorMessage: string;
};
function processResponse(response: SuccessResponse | ErrorResponse) {
// エラー用の目印(errorCode)が含まれているかで分岐する
if (“errorCode” in response) {
console.error(`エラーが発生しました: ${response.errorMessage}`);
return;
}
// ここに来たということは、正常なデータ!
console.log(“成功データ:”, response.data);
}
実務のコードも、基本の考え方はさっきの「動物園の例」とまったく同じです。複雑なシステムであっても、小さな「目印のチェック」の積み重ねでできているんです。
—
おわりに
TypeScriptの型システムは、最初は私たちプログラマーの邪魔をする「厳しい監視員」のように感じられるかもしれません。でも、本当は違います。TypeScriptは、「あなたのコードが未来でバグを起こして泣かないように、そっと手を差し伸べてくれる優しい相棒」なんです。
`in` 演算子を使った型ガードは、そんな相棒と心を通わせるための素晴らしいコミュニケーションツールの一つ。
「このオブジェクト、ちゃんとこのプロパティ持ってるかな?」と不安になったら、ぜひ思い出して使ってみてくださいね。あなたのTypeScriptライフが、少しでも楽しく、軽やかになりますように!

コメント