1. ホーム
  2. JavaScript

【JavaScript】typeof・instanceof・Array.isArray の使い方|データ型を判定する方法

Share

「この変数の中身は文字列なのか、それとも配列なのか」を確かめたい場面は、JavaScriptを書いているとよく出てきます。関数に渡された引数の種類によって処理を分けたいときや、外部から受け取ったデータの形をチェックしたいときなどです。JavaScriptにはデータ型を判定する方法がいくつかあり、代表的なのが typeof・instanceof・Array.isArray の3つです。ただしそれぞれ得意分野と落とし穴があり、使いどころを間違えると誤判定します。この記事では初心者〜中級者の方に向けて、3つの違いと正しい使い分け、より厳密な判定方法までを、動くコードで確認しながら解説します。

判定方法によって得意分野が違う

先に全体像をつかんでおきましょう。3つの判定方法は、それぞれ見ているものが異なります。typeof は値の「大まかな種類」を文字列で返す演算子で、文字列・数値・真偽値といったプリミティブ(基本的な値)の判定に向いています。instanceof は「あるコンストラクタから作られたインスタンスかどうか」を真偽値で返す演算子で、クラスのインスタンスや Date などの組み込みオブジェクトの判定に使います。そして Array.isArray は「配列かどうか」だけを確実に判定する専用のメソッドです。

まずは代表的な値に対して、3つがどんな結果を返すかを見比べてみましょう。同じ値でも判定方法によって結果が変わる様子が分かります。細かい理由はこのあと順番に説明します。

値typeofinstanceof ObjectArray.isArray
"hello"“string”falsefalse
42“number”falsefalse
true“boolean”falsefalse
undefined“undefined”falsefalse
null“object”falsefalse
[1, 2, 3]“object”truetrue
{ a: 1 }“object”truefalse
new Date()“object”truefalse
function(){}“function”truefalse

typeof の列を見ると、配列・オブジェクト・Date・そして null までもが同じ "object" になっている点に気づきます。この「ざっくりしすぎている」ところが typeof の弱点であり、後半で扱うつまずきポイントの中心です。それでは各方法を順に見ていきましょう。

typeof でプリミティブを判定する

typeof は値の種類を表す文字列を返す演算子です。typeof 値 と書くだけで使え、返ってくる文字列は次の8種類のいずれかです。文字列・数値・真偽値といったプリミティブを判定するときに最も手軽で、こうした用途にはこれが第一候補になります。

返る文字列対象となる値
"string"文字列
"number"数値(NaN や Infinity も含む)
"boolean"true / false
"undefined"undefined、未定義のプロパティ
"bigint"BigInt(10n のような値)
"symbol"Symbol
"function"関数(クラスも含む)
"object"上記以外のオブジェクト、および null
type-check.js
console.log(typeof "hello");   // "string"
console.log(typeof 42);        // "number"
console.log(typeof true);      // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof 10n);       // "bigint"
console.log(typeof Symbol());  // "symbol"
console.log(typeof function () {}); // "function"

// 数値であることの判定はこう書ける
const value = 100;
if (typeof value === "number") {
  console.log("これは数値です");
}

戻り値は必ず文字列なので、typeof value === "number" のように文字列と比較して判定します。ここでよくある書き間違いが、比較対象を number(クォートなし)と書いてしまうことです。それでは変数扱いになってしまうため、必ず "number" のように文字列で書きます。

未宣言の変数でもエラーにならない

typeof には他の演算子にない安全な特徴があります。まだ宣言されていない変数に対して使っても、エラーにならず "undefined" を返すのです。通常、宣言されていない変数を参照すると ReferenceError が発生しますが、typeof だけは例外的にこれを起こしません。そのため「そもそもこの変数が存在するかどうか」を安全に確かめる用途に使えます。

type-check.js
// notDeclared はどこにも宣言されていない変数

// console.log(notDeclared);        // ReferenceError で止まる
console.log(typeof notDeclared);    // "undefined"  エラーにならない

// この性質を使って安全に存在チェックできる
if (typeof notDeclared === "undefined") {
  console.log("この変数は定義されていません");
}

typeof null が “object” になる落とし穴

typeof で最も有名な落とし穴が、typeof null が "object" を返すことです。これはJavaScriptの初期からある仕様上の不具合として知られており、後方互換性のために今も修正されずに残っています。つまり typeof だけでは null とオブジェクトを区別できません。null かどうかを確かめたいときは、素直に === null で比較します。

type-check.js
console.log(typeof null); // "object"  ← 本来は "null" が自然だが仕様上こうなる

// null 判定は typeof ではなく === で行う
const value = null;
if (value === null) {
  console.log("これは null です");
}

// 「オブジェクトかつ null ではない」を確かめる定番の書き方
if (typeof value === "object" && value !== null) {
  console.log("これは null 以外のオブジェクトです");
}

あわせて覚えておきたいのが null と undefined の区別です。どちらも「値がない」ことを表しますが、typeof undefined は "undefined"、typeof null は "object" と結果が分かれます。両方をまとめて「値が入っていない」と判定したいなら、value == null(等価演算子 == をひとつだけ使う)と書くと null と undefined の両方で true になります。

配列も関数も “object” 側に寄る

もう一つ押さえたいのは、配列も typeof では "object" になることです。冒頭の表のとおり、配列・プレーンなオブジェクト・Date などはすべて "object" でひとくくりにされ、typeof では見分けられません。例外は関数で、関数だけは "function" という専用の結果を返します。したがって「配列かどうか」を typeof で判定することはできず、次に紹介する専用の方法が必要になります。

Array.isArray で配列を判定する

配列かどうかを調べたいときの正解が Array.isArray です。引数に渡した値が配列であれば true、それ以外なら false を返します。typeof では配列とオブジェクトの区別がつかない問題を、このメソッド一つで解決できます。

type-check.js
console.log(Array.isArray([1, 2, 3])); // true
console.log(Array.isArray([]));        // true  空配列も配列
console.log(Array.isArray({ a: 1 }));  // false オブジェクトは配列ではない
console.log(Array.isArray("abc"));     // false 文字列も配列ではない
console.log(Array.isArray(null));      // false

// 引数が配列のときだけ処理したい場合
function sum(list) {
  if (!Array.isArray(list)) {
    throw new Error("配列を渡してください");
  }
  return list.reduce((a, b) => a + b, 0);
}

instanceof Array より堅牢な理由

配列は次に説明する instanceof を使って value instanceof Array と判定することもできます。多くの場合これでも動きますが、Array.isArray の方が堅牢です。理由は、instanceof が実行環境(レルムと呼ばれる領域)をまたぐと正しく判定できないことにあります。たとえばページ内の iframe の中で作られた配列を、外側のページで受け取ると、両者は別々の Array コンストラクタを持つため instanceof Array が false になってしまいます。Array.isArray はこうした環境の違いに影響されず、どこで作られた配列でも正しく true を返します。

「配列かどうか」を判定したい場面では、迷わず Array.isArray を使うのが安全です。

instanceof でインスタンスを判定する

instanceof は「その値が、指定したコンストラクタから作られたインスタンスか」を判定する演算子です。値 instanceof コンストラクタ の形で書き、真偽値を返します。仕組みとしては、値のプロトタイプチェーン(オブジェクトが継承のためにたどっていく連鎖)をさかのぼり、そこに指定したコンストラクタの prototype が含まれているかを調べています。そのため、自分で定義したクラスのインスタンスや、Date・RegExp といった組み込みオブジェクトの判定に向いています。

type-check.js
class Animal {}
class Dog extends Animal {}

const pochi = new Dog();

console.log(pochi instanceof Dog);    // true
console.log(pochi instanceof Animal); // true  継承元でも true になる
console.log(pochi instanceof Object); // true  すべてのオブジェクトの元をたどる

// 組み込みオブジェクトの判定にも使える
console.log(new Date() instanceof Date);     // true
console.log(/abc/ instanceof RegExp);        // true
console.log([1, 2] instanceof Array);        // true

継承関係のある Dog のインスタンスが、親クラスの Animal に対しても true を返している点に注目してください。instanceof はプロトタイプチェーンをたどるため、継承元のクラスでも true になります。この性質は「ある系統に属するか」を確かめたいときに便利です。

プリミティブには使えない

注意したいのは、instanceof がオブジェクト(インスタンス)専用だということです。文字列リテラルや数値リテラルのようなプリミティブに対しては、たとえ String や Number と比較しても false を返します。プリミティブとして書いた値は、コンストラクタから new で作ったインスタンスではないためです。

type-check.js
console.log("hello" instanceof String); // false  文字列リテラルはインスタンスではない
console.log(42 instanceof Number);       // false

// new で明示的に作ると true になるが、通常こう書くことは推奨されない
console.log(new String("hello") instanceof String); // true

このため、文字列・数値・真偽値といったプリミティブの判定には instanceof ではなく typeof を使います。instanceof はあくまで「インスタンスかどうか」を見る道具だと覚えておきましょう。先に触れたとおり、実行環境(iframe など)をまたぐと判定に失敗することがある点も、頭の片隅に置いておくとよいでしょう。

Object.prototype.toString.call で厳密に見分ける

「null なのか配列なのか Date なのか、まとめて正確に見分けたい」というときに使えるのが Object.prototype.toString.call(値) です。これは値ごとに "[object 型名]" という形式の文字列を返します。typeof では区別できなかった null・配列・Date なども、それぞれ違う結果になるため、汎用的な型判定に向いています。

type-check.js
const toType = (v) => Object.prototype.toString.call(v);

console.log(toType("hello"));   // "[object String]"
console.log(toType(42));        // "[object Number]"
console.log(toType(true));      // "[object Boolean]"
console.log(toType(undefined)); // "[object Undefined]"
console.log(toType(null));      // "[object Null]"   ← null もちゃんと区別できる
console.log(toType([1, 2, 3])); // "[object Array]"  ← 配列も区別できる
console.log(toType({ a: 1 }));  // "[object Object]"
console.log(toType(new Date()));// "[object Date]"   ← Date も区別できる
console.log(toType(() => {}));  // "[object Function]"

Object.prototype.toString をそのまま呼ぶのではなく、call(値) を使って判定したい値に適用しているのがポイントです。この一行を使い回せば、あらゆる値の種類をひとつの方法で見分けられます。型名の部分だけを取り出したいときは、正規表現や slice で "Array" のような文字列を抜き出す関数にしておくと便利です。

NaN の判定は Number.isNaN で行う

型判定に関連してつまずきやすいのが NaN(Not-a-Number、数値でない値)です。typeof NaN は "number" を返すため、型としては数値の仲間です。ところが NaN には「自分自身とも等しくない」という特殊な性質があり、value === NaN のような比較では判定できません。そこで専用の Number.isNaN を使います。

type-check.js
console.log(typeof NaN);          // "number"  型としては数値
console.log(NaN === NaN);         // false     自分自身とも等しくない

console.log(Number.isNaN(NaN));   // true   これが正しい NaN 判定
console.log(Number.isNaN(123));   // false
console.log(Number.isNaN("abc")); // false  文字列は変換せず、NaN そのものではない

// 数値への変換に失敗したかどうかの確認に使える
const n = Number("12px"); // NaN
if (Number.isNaN(n)) {
  console.log("数値に変換できませんでした");
}

グローバルの isNaN という関数もありますが、こちらは引数を数値に変換してから判定するため、isNaN("abc") が true になるなど直感に反する結果を返します。「値が本当に NaN かどうか」を確かめたいときは、変換を行わない Number.isNaN を使うのが安全です。

結局どれを使えばいいのか

ここまでの内容をふまえると、判定したい対象ごとに選ぶべき方法が整理できます。文字列・数値・真偽値・undefined といったプリミティブは typeof、配列は Array.isArray、自作クラスや Date などの組み込みインスタンスは instanceof、そして null と非nullオブジェクトの区別や、あらゆる種類を細かく見分けたい厳密な判定は Object.prototype.toString.call という具合です。次の表に用途と選ぶべき方法をまとめておきます。

判定したいもの使う方法
文字列・数値・真偽値などのプリミティブtypeof
未宣言の変数の存在チェックtypeof(エラーにならない)
配列かどうかArray.isArray
クラスのインスタンス・Date などの組み込みinstanceof
null かどうか=== null
あらゆる種類を厳密に見分けるObject.prototype.toString.call
NaN かどうかNumber.isNaN

まとめ

JavaScriptのデータ型判定は、対象に合った方法を選ぶことが大切です。typeof はプリミティブの判定に手軽で、未宣言の変数でもエラーにならない一方、null や配列がまとめて "object" になる点に注意します。配列の判定は環境をまたいでも確実な Array.isArray が正解です。instanceof はプロトタイプチェーンをたどってインスタンスかどうかを見るため、クラスや組み込みオブジェクトの判定に向いていますが、プリミティブや iframe をまたぐケースでは使えません。そして null・配列・Date まで正確に見分けたいときは Object.prototype.toString.call が万能です。それぞれの得意分野と落とし穴を押さえれば、型判定で迷うことはなくなります。

参考ページ