TypeScriptを触りはじめた頃、多くの人が最初に「うっ……」とつまずくポイントがあります。それが、「お前、本当にそのプロパティ持ってるのかよ問題」です。
APIから返ってきたデータや、ユーザーが入力した曖昧なオブジェクト。こいつを安全に料理したいのに、TypeScriptの compiler(コンパイラ)は「いや、それ undefined かもしれないし……」と、なんだか冷たくエラーを出してくる。
「型安全って素晴らしいって聞いたけど、なんか窮屈だな……」
そんなふうに肩を落としているあなたへ。大丈夫ですよ、今日でそのモヤモヤは綺麗さっぱり解消されます。今回は、オブジェクトの世界の「持ち物検査」をしてくれる魔法の呪文、`in` 演算子による型ガードについて、一緒に優しく紐解いていきましょう!
—
そもそも「型ガード」ってなに? お買い物の例えで考えてみる
TypeScriptの型(Type)は、いわば「お守り」のようなものです。「この箱の中には、必ずりんごが入っています」と保証してくれるからこそ、安心して皮を剥く(プロパティを使う)ことができます。
でも、世の中には「りんごが入っているかもしれないし、みかんが入っているかもしれない、あるいは空っぽかもしれない」という、中身がパッと見でわからない怪しい段ボール箱(`any` や `unknown`、あるいは複数の型が合体した共用体型 / Union Types)がたくさん存在します。
ここで想像してみてください。
あなたはスーパーの店員さんです。お客さんが持ってきた箱のなかに「りんご」が入っているか確かめたいとき、どうしますか?
そう、箱を開けて、中を覗き込みますよね。
TypeScriptの世界でもまったく同じです。「このオブジェクトの中に、特定のプロパティ(目印)が入っているか?」をその場でチェックして、「おっ、入ってるじゃん!じゃあこの箱の中身はりんごとして扱っていいよ」と、TypeScriptに安全なルートを教えてあげること。これが「型ガード」の正体です。
—
主役登場! `in` 演算子ってどう使うの?
JavaScriptにも昔からある `in` 演算子ですが、TypeScriptの世界では最強の「持ち物検査官」に変貌します。
書き方はとってもシンプルです。
“プロパティ名” in 対象のオブジェクト
これだけで、「このオブジェクトの中に、そのプロパティが存在するかどうか」を `true` か `false` で判定してくれます。そして、ここからがTypeScriptのすごいところなのですが、`if` 文の中でこのチェックを行うと、そのブロックの中だけ自動的に型を絞り込んで(Narrowing)くれるんです。
百聞は一見にしかず。実際のコードを見てみましょう。
—
実践!ネコちゃんとワンちゃんを安全に愛でる方法
例えば、アプリの中で「ネコ」と「犬」という、ちょっと違う特徴を持つ動物のデータムを扱うとします。
// ネコちゃんは「ゴロゴロ鳴く(purr)」という特技を持っている
interface Cat {
name: string;
purr: () => void;
}
interface Dog {
name: string;
bark: () => void;
}
// どっちが来るかわからない、動物さん
type Animal = Cat | Dog;
ここで、やってきた動物を撫でる関数を作るとしましょう。
function playWithAnimal(animal: Animal) {
// ああっ!animal.purr() といきなり書くと、
// 「Dogには purr がないかもしれないよ!」ってTypeScriptに怒られちゃいます。
// そこで登場するのが in 演算子!
if (“purr” in animal) {
// このブロックの中に入ってきたということは、
// TypeScriptは「おっ、この子には purr があるから Cat だな!」と理解します。
animal.purr(); // 安心してゴロゴロさせられる!
} else {
// こっちは自動的に Dog だと確定します
animal.bark(); // ワン!
}
}
すごくないですか?
「`”purr” in animal`」という、たったこれだけのコードを書くだけで、TypeScriptが「あ、持ち物検査に合格したから、こっちの型ですね」と、自動で安全な道に案内してくれるんです。
—
ここでよくある「つまずきポイント」をそっとフォロー
「なるほど、便利そう!じゃあどんなオブジェクトでも `in` 演算子でパパッとチェックしちゃえば完璧だね!」
……と言いたいところですが、実務の現場では、うっかりやってしまいがちな「落とし穴」があります。初心者さんがよくハマるポイントを2つ、こっそりシェアしておきますね。
落とし穴1: `any` や `unknown` のままだと意味がない?
`in` 演算子は、型がボヤっとしている `unknown` 型の変数に対しても絶大な効果を発揮します。
ただし、もしデータが完全に崩壊している `any` だと、TypeScriptはチェックをサボってしまうので、なるべく `unknown` や複数の型を組み合わせた `Union型` に対して使うのが、TypeScriptの恩恵を一番受けられる王道パターンです。
落とし穴2: プロパティのスペルミスに気をつけて!
`in` 演算子の左側に書く文字列は、当然ですが完全一致でなければいけません。
// うっかり大文字・小文字を間違えると……
if (“Purr” in animal) { // “purr” と書くべきところを “Purr” にしてしまった!
// ここに入ってこなくて「あれ?動かないな……」と深夜に頭を抱えることになります(経験者談)
}
エディタの補完機能をうまく使いながら、プロパティ名に間違いがないか優しく確認してあげてくださいね。
—
まとめ:`in` 演算子で、もっと優しく安全なコードを書こう
今回は、TypeScriptの基本の型定義の延長線上で、オブジェクトの型を安全に絞り込む `in` 演算子について解説しました。
- 型ガードとは、曖昧なデータの「持ち物検査」をして安全な道に導くテクニック。
- `in` 演算子を使えば、「このプロパティ持ってる?」を判定するだけで、TypeScriptが勝手に型を絞り込んでくれる。
- エラーに怯える必要はもうありません。コンパイラと仲良くなって、怖くない型安全な世界を楽しもう!
最初は「なんだかルールが多くて窮屈だな」と感じるかもしれないTypeScriptですが、こうした便利な道具(型ガード)の使い方を知っていくと、まるで優秀なペアプログラマーが隣で「そこ危ないよ!」とそっと支えてくれているような心強さに変わっていきます。
あなたのWeb制作・開発の旅が、少しでも快適で楽しいものになりますように。
焦らず、一歩ずつ、自分のペースで進んでいきましょうね!

コメント