1. ホーム
  2. JavaScript

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

Share

「この変数の中身は文字列なのか、それとも配列なのか」を確かめたい場面は、JavaScriptを書いているとよく出てきます。関数に渡された引数の種類によって処理を分けたいときや、外部から受け取ったデータの形をチェックしたいときなどです。JavaScriptにはデータ型を判定する方法がいくつかあり、代表的なのが typeofinstanceofArray.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"数値(NaNInfinity も含む)
"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 以外のオブジェクトです");
}

あわせて覚えておきたいのが nullundefined の区別です。どちらも「値がない」ことを表しますが、typeof undefined"undefined"typeof null"object" と結果が分かれます。両方をまとめて「値が入っていない」と判定したいなら、value == null(等価演算子 == をひとつだけ使う)と書くと nullundefined の両方で 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 Arrayfalse になってしまいます。Array.isArray はこうした環境の違いに影響されず、どこで作られた配列でも正しく true を返します。

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

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

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

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 がオブジェクト(インスタンス)専用だということです。文字列リテラルや数値リテラルのようなプリミティブに対しては、たとえ StringNumber と比較しても 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 が万能です。それぞれの得意分野と落とし穴を押さえれば、型判定で迷うことはなくなります。

参考ページ