【入門編】 instanceofによる型ガード – TypeScript実践ガイド

TypeScriptの世界へようこそ!チーフアーキテクトの私です。

TypeScriptを書き始めた頃って、コンパイラから「このままだと型がわからないよ!」って急に怒られて、どうすればいいか途方に暮れちゃいますよね。特に `any` から脱却しようともがいている時、型の壁にぶつかって頭を抱えるのは、誰もが通るおめでたい(?)最初の関門です。

大丈夫ですよ、安心してください。今日はそのモヤモヤをスッキリ解消する、実務でもめちゃくちゃお世話になる「`instanceof`(インスタンスオブ)による型ガード」について、おしゃべりするみたいに優しく紐解いていきましょう!

—

そもそも「型ガード」ってなに?

TypeScriptの仕事は、コードを実行する前に「ここ、エラーになりそうじゃない?」って事前に教えてくれるお目付役です。

例えば、目の前に「何が入っているかわからない段ボール箱」があるとします。中身がネコちゃんなのか、それとも頑丈な鉄アレイなのか、外から見ただけではわかりませんよね?
もしネコちゃんだと思って激しく箱を揺すったら大惨事になりますし、逆に鉄アレイだと思って「よしよし」と撫でようものなら手が折れちゃいます。

プログラムの世界もこれと全く一緒です。
「この変数、string型なのか、それとも私たちが自作した特別なクラスのデータなのか、パッと見じゃわからないよ!」とTypeScriptが困っている時に、「ちょっと待って、今から中身を確認するから!」と安全地帯を作ってあげる作業。これが型ガードです。

—

`instanceof` は「この子、〇〇家の血筋です!」を見抜く探偵

今回主役として登場する `instanceof` は、「あるモノ(オブジェクト)が、特定のクラスから生まれた子供(インスタンス)かどうか」を判定してくれる頼れる探偵です。

JavaScriptやTypeScriptには「クラス(Class)」という、いわゆる“設計図”を作る仕組みがありますよね。
例えば、「お猫様クラス」という設計図から生まれた「ミケ」という実体がいるとします。

`instanceof` を使うと、「このミケちゃんは、ちゃんと『お猫様クラス』の設計図から作られた子ですか?」というのを、プロトタイプチェーン(JavaScriptの裏側の仕組みですね)をたどって厳密に調べてくれるんです。

—

身近なたとえ話でお買い物に例えてみよう

Web制作の現場でもよくあるシチュエーションを考えてみましょう。
あなたは今、オンラインショップのカートシステムを作っています。

そこには、

  • 普通の商品(本や服など)
  • ちょっと特別な「生鮮食品」(お肉や野菜。消費期限がある!)

という2種類のデータが混ざって流れてきます。画面に「消費期限」を表示したいのですが、普通の本にまで「消費期限:明日まで」なんて表示したら、ユーザーが混乱してしまいますよね。

ここで `instanceof` の出番です。流れてきた商品が「生鮮食品クラス」のインスタンスかどうかを `instanceof` でパッと見分けて、生鮮食品のときだけ専用の処理(消費期限の表示)をしてあげましょう。

—

実際にコードを書いて動かしてみよう!

百聞は一見に如かず。エディタを開いて、以下のコードをコピペしてみてください。TypeScriptの優しい世界が実感できますよ。

// 1. 普通の商品を表すクラス(設計図)
class NormalItem {
constructor(public name: string, public price: number) {}
}

// 2. 生鮮食品を表すクラス(設計図)
// NormalItemの性質を引き継ぎつつ、「消費期限」という特別なプロパティを持っています
class FreshItem extends NormalItem {
constructor(name: string, price: number, public expiryDate: string) {
super(name, price); // 親クラスの仕組みを受け継ぎます
}
}

// カートの中身をチェックする関数
// 引数の item は「NormalItem なのか FreshItem なのか、今はまだ分からないよ」という状態です
function displayItemDetails(item: NormalItem) {

// ここで `instanceof` を使った型ガードを発動!
// 「この item は FreshItem クラスのインスタンスですか?」と判定します
if (item instanceof FreshItem) {
// 【TypeScriptの魔法のゾーン】
// この中に入った瞬間、TypeScriptは「あ、この子は FreshItem だね!」と理解します。
// なので、NormalItemには無い `expiryDate` にアクセスしても怒られません!
console.log(`【生鮮食品】${item.name} / 価格: ${item.price}円`);
console.log(`⚠️ 消費期限: ${item.expiryDate} までにお召し上がりください!`);

} else {
// 【通常商品のゾーン】
// ここは FreshItem ではなかった(=普通の NormalItem である)と判定されたゾーンです
console.log(`【通常商品】${item.name} / 価格: ${item.price}円`);
// ※もしここで item.expiryDate にアクセスしようとすると、TypeScriptが
// 「いやいや、NormalItemにそんなプロパティはないよ!」とちゃんと止めてくれます。
}
}

// — 動作テスト —
const myBook = new NormalItem(“TypeScript実践入門”, 3200);
const myMeat = new FreshItem(“極上ステーキ肉”, 1980, “明日”);

console.log(“— 本のデータをチェック —“);
displayItemDetails(myBook);

console.log(“\n— お肉のデータをチェック —“);
displayItemDetails(myMeat);

コードのここがポイント!

`if (item instanceof FreshItem)` の扉をくぐった瞬間、TypeScriptの賢い頭脳(型推論)が切り替わります。
扉の中では、エディタの補完機能(IntelliSense)が `expiryDate` を優しく提案してくれるはずです。これ、実務で書いてて一番気持ちいい瞬間の一つなんですよね。

—

つまずきやすいポイントと注意点

ここで、現場で初心者がやりがちな「ちょっとした罠」についてもシェアしておきますね。

1. プリ型(string, number, boolean)には使えない

`instanceof` はあくまで「クラスから作られたオブジェクト(インスタンス)」を見分けるためのものです。
そのため、`typeof item === “string”` のような基本の型(プリミティブ型)の判定には使えません。基本の型には `typeof`、クラスやオブジェクトの判定には `instanceof` と、使い分けるのが鉄則です。

2. 外部からやってきたデータ(APIのレスポンスなど)の罠

バックエンドから送られてきたJSONデータ(ただの素のオブジェクト `{ name: “…”, price: … }`)は、そのままでは `FreshItem` クラスのインスタンスではありません。
「えっ、ちゃんと形は同じなのに `instanceof` が `false` を返すんだけど!?」と最初は焦るポイントです。クラスのインスタンスとして扱いたい場合は、きちんと `new FreshItem(…)` のようにインスタンス化してあげる必要がある、という点はぜひ覚えておいてくださいね。

—

まとめ

  • `instanceof` は、オブジェクトがどのクラスの血筋(インスタンス)かを見抜く探偵。
  • `if (item instanceof 〇〇)` で囲うだけで、TypeScriptが自動で安全に型を絞り込んでくれる(型ガード)。
  • 分からなくなったら、「段ボール箱の中身を確認する儀式」だと思って使ってみよう!

最初は難しく感じるかもしれませんが、型ガードを使いこなせるようになると、TypeScriptを書く時間がぐっと楽しく、そして何より「バグに怯えなくていい安心感」が手に入ります。

焦らず、一歩ずつ自分のペースで進んでいきましょう。あなたのTypeScriptライフを、チーフアーキテクトとして陰ながら応援しています!

コメント

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