🤩

【TypeScript】instanceof演算子について

に公開

初めに

instanceof について自分自身でまとめてみようと思い、この記事を書いてます。
(初心者のため優しい目で見てください)

instanceofとは?

オブジェクトが特定のクラスから生成されたかどうかを実行時に判定する。つまり、クラスベースの実行時型チェックとして便利な演算子です。

構文

obj instanceof クラス名
  • obj → 判定したいオブジェクト
  • クラス名 → 判定対象のクラス

基本的な使い方

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name);
  }
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog("Pochi");

// クラスベースの実行時型チェック
console.log(dog instanceof Dog);    // true → dog は Dog のインスタンス
console.log(dog instanceof Animal); // true → Dog は Animal を継承しているので判定される
console.log(dog instanceof Object); // true → すべてのオブジェクトは Object のインスタンス
console.log(dog instanceof Array);  // false → Dog は Array とは無関係

// 実際の動作も確認できる
dog.speak(); // "Pochi barks."

ポイント

  • 継承関係も含めて判定できる
  • instanceof は クラスベースの実行時型チェック に使える

型ナローイングとinstanceof

TypeScript では instanceof を使うと、条件分岐内でオブジェクトの型を自動で絞り込むことができます。
これを型ナローイングと呼びます。

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name} barks loudly!`);
  }
}

class Cat extends Animal {
  meow() {
    console.log(`${this.name} meows.`);
  }
}

function makeSound(animal: Animal) {
  // 型ナローイング
  if (animal instanceof Dog) {
    // このブロック内では animal は Dog 型として扱える
    animal.bark(); // OK
  } else if (animal instanceof Cat) {
    // このブロック内では animal は Cat 型として扱える
    animal.meow(); // OK
  } else {
    // その他の Animal
    animal.speak(); // Animal 型として扱える
  }
}

const dog = new Dog("Pochi");
const cat = new Cat("Tama");

makeSound(dog); // "Pochi barks loudly!"
makeSound(cat); // "Tama meows."

ポイント

  • 実行時にオブジェクトの型を判定しつつ、安全にサブクラス固有の処理が書ける

エラーハンドリングでの活用

function handleError(err: unknown) {
  if (err instanceof Error) {
    // 型ナローイングされているので Error のプロパティに安全にアクセス可能
    console.log("Error message:", err.message);
  } else {
    console.log("Caught an unknown error:", err);
  }
}

// 実際の使用例
try {
  throw new Error("Something went wrong");
} catch (err: unknown) {
  handleError(err); // 型ナローイングされた安全な処理
}

// さらに未知の型のエラーも安全に処理できる
try {
  throw "string error";
} catch (err: unknown) {
  handleError(err);
}

ポイント

  • unknown 型のエラーを受け取ったときに、安全に Error のプロパティにアクセスできる
  • どんな unknown 型のエラーも handleError に渡すだけで安全に処理可能
  • 型安全に処理できるので、バグを防げる

注意点(例)

  1. インターフェースには使えない
    TypeScript の インターフェースはコンパイル時に消える。実行時には存在しないので instanceof で判定できない
  2. プリミティブ型には使えない
    string, number, boolean などのプリミティブには使えない
  1. 右辺が関数(コンストラクタ)でないと実行時に TypeError になる
    右側が関数/オブジェクトでない(例えば null や undefined)とランタイム例外になります。
const obj = {};
// TypeError: Right-hand side of 'instanceof' is not callable
console.log(obj instanceof null);
  1. null / undefined は常に false
console.log(null instanceof Object); // false(エラーにならない)
console.log(undefined instanceof Object); // false(エラーにならない)
  • 判定対象(左側)が nullundefined の場合、常に false を返す
  • エラーにはならない点に注意です。

instanceofの反転

instanceof の結果を逆に判定したいときは、次の手順で書きます。

  1. 値 instanceof クラス名 の全体を かっこ ( ) で囲む
  2. その先頭に !(NOT 演算子) をつける

例1

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

const dog = new Dog();

if (!(dog instanceof Cat)) {
  console.log("dog は Cat ではありません"); 
}

ポイント

  • !を使って反転させるだけ
  • 「あるクラスのインスタンスではない場合」の条件分岐として便利

例2

class CustomError extends Error {}
class ValidationError extends Error {}

function handleError(err: unknown) {
  if (!(err instanceof CustomError)) {
    // CustomError 以外のエラーはここでまとめて処理
    console.error("Unexpected error:", err);
  } else {
    console.log("Custom error:", err.message);
  }
}

handleError(new ValidationError("Invalid input")); // Unexpected error: ...
handleError(new CustomError("Something custom"));  // Custom error: Something custom

ポイント

  • 特定のエラークラスだけ別処理して、それ以外をまとめて処理する場合に便利

まとめ

  • instanceof は オブジェクトが特定のクラスのインスタンスかを実行時に判定する演算子
  • 継承関係も含めて判定可能 → サブクラスのチェックや型ナローイングに便利
  • TypeScript では instanceof を使うと 型ナローイング が働き、サブクラス固有の処理を安全に実行できる
  • エラーハンドリングにも活用可能 → unknown 型のエラーを安全に扱える
  • ! を使えば 「特定のクラスではない場合」の判定 も簡単にできる
  • 注意点
    • インターフェースには使えない(実行時に存在しないため)
    • プリミティブ型には使えない(ただし、String, Number, Boolean の ラッパーオブジェクトには使える。)
    • 右辺が関数(コンストラクタ)でないと実行時に TypeError になる
    • null / undefined は常に false

Discussion