JavaScriptの型判定とは、値が文字列・数値・配列・nullなどのどれにあたるかをコードで調べることで、プリミティブ(文字列や数値などの基本の値)はtypeof、配列はArray.isArray、nullは === null、自作クラスはinstanceofと、調べたい相手ごとに道具を使い分けるのが基本です。 typeofだけで済ませようとすると、nullも配列もDateもすべて "object" と返ってくるため、「何を調べたいか」から書き方を選ぶと迷いません。

この記事では、36種類の値に4つの判定方法をすべて当てた実行結果を1枚の表にしました。あわせて「nullかundefinedか」「空か」「数値として有効か」をそのまま貼って使えるコード、iframeをまたぐとinstanceofがfalseになる様子の再現、最後に汎用のgetType関数を載せています。実行結果はすべて Node.js v22.22.2 と Chromium 141(Playwright 1.56)で実際に動かして取得したものです。

JavaScript型判定の早見表

まず「何を調べたいか」から書き方を引ける表です。迷ったらこの表の「おすすめの書き方」をそのまま使えば用が足ります。

調べたいこと おすすめの書き方 typeofだけだとどうなるか
文字列か typeof v === 'string' そのまま使える
数値か(NaNも含む) typeof v === 'number' そのまま使える
計算に使える数値か typeof v === 'number' && Number.isFinite(v) NaNとInfinityも "number" になる
NaNか Number.isNaN(v) "number" になる
整数か Number.isInteger(v) "number" になる
真偽値か typeof v === 'boolean' そのまま使える
BigIntか typeof v === 'bigint' そのまま使える
Symbolか typeof v === 'symbol' そのまま使える
undefinedか v === undefined typeof v === 'undefined' でも可
nullか v === null "object" になる
nullまたはundefinedか v == null 2回の比較が必要
関数か typeof v === 'function' そのまま使える(クラスも "function")
配列か Array.isArray(v) "object" になる
ふつうのオブジェクトか v !== null && typeof v === 'object' && !Array.isArray(v) 配列やnullも "object" になる
Date・Map・Set・RegExpか v instanceof Date など "object" になる
自作クラスのインスタンスか v instanceof User "object" になる
Promise(thenできる値)か v != null && typeof v.then === 'function' "object" になる
エラーか v instanceof Error(別の窓の値も見るなら Error.isError(v)) "object" になる
組み込みの種類名を知りたい Object.prototype.toString.call(v) 8種類の文字列しか返らない

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript型判定 typeof・instanceof・null早見表」

4つの判定方法の役割と違い

JavaScriptの型判定でよく使う道具は4つあります。それぞれ「何を見ているか」が違うので、まずそこを押さえます。

方法 何を見ているか 返すもの 得意な相手 気をつける点
typeof v 値の大まかな種類 8種類の文字列 プリミティブ、関数 nullと配列などは "object"
v instanceof C プロトタイプチェーン(親をたどる鎖)に C.prototype があるか true / false 自作クラス、Date、Map プリミティブは常にfalse。別の窓(iframe)の値はfalse
Array.isArray(v) 本物の配列かどうか true / false 配列 型付き配列(Uint8Arrayなど)はfalse
Object.prototype.toString.call(v) 組み込みの種類を表すタグ [object Xxx] 形式の文字列 組み込みオブジェクト全般 自作クラスは [object Object]。タグは書き換えられる

typeofが返す文字列は "undefined" "boolean" "number" "bigint" "string" "symbol" "function" "object" の8種類だけです。これ以外の文字列("array" や "null" など)は返りません。

flowchart TD
  A[値 v を調べたい] --> B{null か undefined?}
  B -- はい --> B1["v === null / v === undefined / v == null"]
  B -- いいえ --> C{プリミティブか関数?}
  C -- はい --> C1["typeof v"]
  C -- いいえ --> D{配列?}
  D -- はい --> D1["Array.isArray(v)"]
  D -- いいえ --> E{自作クラスや Date / Map?}
  E -- はい --> E1["v instanceof クラス"]
  E -- いいえ --> F["Object.prototype.toString.call(v)"]

36種類の値に4つの判定を当てた実行結果

ここがこの記事の中心です。同じ値に対して4つの方法が何を返すかを、Chromium 141 のページ内で実行して表にしました(DOMを使わない行は Node.js v22.22.2 でも同じ結果です)。

表を作ったコードは次のとおりです。ブラウザーの開発者ツールのコンソールに貼ると、手元でも同じ表を確認できます。

class User {}
const ts = v => Object.prototype.toString.call(v);
const samples = [
  ['null', null], ['undefined', undefined], ['true', true], ['42', 42], ['NaN', NaN],
  ['10n', 10n], ['"abc"', 'abc'], ['Symbol("s")', Symbol('s')],
  ['{}', {}], ['[]', []], ['() => {}', () => {}], ['class User {}', User],
  ['new User()', new User()], ['new Date()', new Date()], ['new Map()', new Map()],
  ['Promise.resolve()', Promise.resolve()],
];
console.table(samples.map(([name, v]) => ({
  値: name,
  typeof: typeof v,
  'instanceof Object': v instanceof Object,
  'Array.isArray': Array.isArray(v),
  toString: ts(v),
})));

実行結果(36種類ぶん)は次の表です。

値 typeof instanceof Object Array.isArray Object.prototype.toString.call
null object false false [object Null]
undefined undefined false false [object Undefined]
true boolean false false [object Boolean]
42 number false false [object Number]
NaN number false false [object Number]
Infinity number false false [object Number]
10n bigint false false [object BigInt]
"abc" string false false [object String]
Symbol("s") symbol false false [object Symbol]
{} object true false [object Object]
[] object true true [object Array]
function(){} function true false [object Function]
() => {} function true false [object Function]
async () => {} function true false [object AsyncFunction]
function*(){} function true false [object GeneratorFunction]
class User {} function true false [object Function]
new User() object true false [object Object]
new Date() object true false [object Date]
/a/ object true false [object RegExp]
new Error("e") object true false [object Error]
new Map() object true false [object Map]
new Set() object true false [object Set]
new WeakMap() object true false [object WeakMap]
Promise.resolve() object true false [object Promise]
new Uint8Array(2) object true false [object Uint8Array]
new ArrayBuffer(2) object true false [object ArrayBuffer]
new Number(1) object true false [object Number]
new String("a") object true false [object String]
new Boolean(false) object true false [object Boolean]
Object.create(null) object false false [object Object]
Math object true false [object Math]
JSON object true false [object JSON]
globalThis(ブラウザー) object true false [object Window]
document.body object true false [object HTMLBodyElement]
document.all undefined true false [object HTMLAllCollection]
new URL("https://example.com") object true false [object URL]

表から読み取れる7つのポイント

表を眺めると、typeofだけでは見分けがつかない組み合わせがはっきりします。

  1. nullはtypeofで "object"。v === null で調べます。toStringなら [object Null] と区別できます。
  2. 配列はtypeofで "object"。Array.isArray(v) だけがtrueを返します。
  3. NaNとInfinityはtypeofで "number"。toStringでも [object Number] なので、Number.isNaN と Number.isFinite で調べます。
  4. クラスはtypeofで "function"。クラスは関数の一種として作られているためです。
  5. 自作クラスのインスタンスはtoStringで [object Object]。クラス名までは出ないので、instanceofを使います。
  6. new Number(1) のようなラッパーオブジェクトはtypeofで "object"。toStringでは [object Number] になり、プリミティブの42と同じ表示になります。
  7. Object.create(null) は instanceof Object がfalse。プロトタイプを持たないオブジェクトだからです。typeofは "object" です。

document.all がtypeofで "undefined" になるのは、昔のWebページとの互換性のためにHTML仕様で特別に決められた挙動です。ふだんのコードで使う場面はほぼありませんが、表の中で唯一「オブジェクトなのにtypeofが "undefined"」になる値として載せました。

typeofの使い方と得意な範囲

typeofは演算子(関数ではなく、+ や ! と同じ仲間)です。typeof v と書いても typeof(v) と書いても同じ結果になります。プリミティブと関数を調べるときはtypeofが最も手軽で確実です。

console.log(typeof 'abc');        // "string"
console.log(typeof 42);           // "number"
console.log(typeof 10n);          // "bigint"
console.log(typeof true);         // "boolean"
console.log(typeof Symbol('s'));  // "symbol"
console.log(typeof undefined);    // "undefined"
console.log(typeof (() => {}));   // "function"
console.log(typeof typeof 1);     // "string"(typeofの結果は必ず文字列)

宣言していない変数にも使える

typeofには、宣言していない変数に使ってもエラーにならないという特徴があります。ブラウザーとNode.jsの両方で動くコードで、window や process があるかを調べるときに役立ちます。

console.log(typeof undeclaredVar); // "undefined"(エラーにならない)

try {
  undeclaredVar; // 直接読むとエラー
} catch (e) {
  console.log(e.name + ': ' + e.message);
}
undefined
ReferenceError: undeclaredVar is not defined

一方、let や const で宣言した変数を、宣言の行より前でtypeofに渡すとエラーになります。これは「一時的デッドゾーン」と呼ばれる、宣言前の変数に触れられない区間のためです。

try {
  console.log(typeof tdz);
  let tdz = 1;
} catch (e) {
  console.log(e.name + ': ' + e.message);
}
ReferenceError: Cannot access 'tdz' before initialization

typeof nullが "object" になる理由

typeof null が "object" を返すのは、JavaScriptの最初の実装で、値の種類を表す内部の目印がオブジェクトとnullで同じだったことに由来します。この挙動に頼ったWebページが多く存在するため、仕様として今もそのまま残っています。nullの判定は v === null と覚えておけば十分です。

オブジェクトかどうかを調べるときは、nullを先に除く形にします。

// 直す前:nullもtrueになる
const isObjectBefore = v => typeof v === 'object';

// 直した後:nullを除く
const isObject = v => v !== null && typeof v === 'object';

console.log(isObjectBefore(null), isObject(null)); // true false
console.log(isObjectBefore({}), isObject({}));     // true true

instanceofの使い方と得意な範囲

v instanceof C は、vのプロトタイプチェーンをたどって C.prototype が見つかるかを調べます。継承していれば親クラスに対してもtrueになります。

class User {}
class Admin extends User {}
const a = new Admin();

console.log(a instanceof Admin);  // true
console.log(a instanceof User);   // true(親クラスもtrue)
console.log(a instanceof Object); // true
console.log(User.prototype.isPrototypeOf(a)); // true(同じ意味の書き方)
console.log(a.constructor.name);  // "Admin"

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

instanceofはオブジェクト専用です。文字列や数値などのプリミティブに使うと、常にfalseが返ります。

console.log('a' instanceof String);             // false
console.log(new String('a') instanceof String); // true
console.log(42 instanceof Number);              // false
console.log(Object.create(null) instanceof Object); // false

文字列かどうかは typeof v === 'string' で調べます。new String('a') のようなラッパーオブジェクトは、ふだんのコードでは作らないのが一般的です。

右側に置けるのは関数(クラス)だけ

instanceofの右側には、コンストラクター(クラスや関数)を置きます。それ以外を置くと次のエラーになります。

try { ({}) instanceof 1; } catch (e) { console.log(e.name + ': ' + e.message); }
try { ({}) instanceof (() => {}); } catch (e) { console.log(e.name + ': ' + e.message); }
TypeError: Right-hand side of 'instanceof' is not an object
TypeError: Function has non-object prototype 'undefined' in instanceof check

2つ目はアロー関数を置いた例です。アロー関数は prototype を持たないため、instanceofの相手にはできません。

Symbol.hasInstanceで判定を自分で決める

クラスに static [Symbol.hasInstance] を定義すると、instanceofの結果を自分で決められます。知っておくと、ライブラリーのコードを読むときに戸惑いません。

class Even {
  static [Symbol.hasInstance](n) { return n % 2 === 0; }
}
console.log(2 instanceof Even); // true
console.log(3 instanceof Even); // false

iframeをまたぐとinstanceofがfalseになる例

instanceofには、別の窓(iframeや別ウィンドウ)で作られた値に対してfalseを返すという性質があります。窓ごとに Array や Date といった組み込みのコンストラクターが別々に用意されていて、プロトタイプも別物だからです。

次のHTMLを保存してブラウザーで開くと、再現できます。

<!DOCTYPE html>
<meta charset="utf-8">
<iframe id="f" srcdoc="<p>x</p>"></iframe>
<script>
  window.addEventListener('load', () => {
    const w = document.getElementById('f').contentWindow; // iframe側のwindow
    const ts = v => Object.prototype.toString.call(v);

    const arr = new w.Array(1, 2, 3); // iframe側のArrayで作った配列
    const d = new w.Date(0);
    const err = new w.Error('x');

    console.log('arr instanceof Array:', arr instanceof Array);
    console.log('arr instanceof w.Array:', arr instanceof w.Array);
    console.log('Array.isArray(arr):', Array.isArray(arr));
    console.log('toString(arr):', ts(arr));
    console.log('d instanceof Date:', d instanceof Date);
    console.log('toString(d):', ts(d));
    console.log('err instanceof Error:', err instanceof Error);
    console.log('Error.isError(err):', Error.isError(err));
    console.log('new w.Object() instanceof Object:', new w.Object() instanceof Object);
    console.log('typeof arr:', typeof arr);
  });
</script>

実行結果(Chromium 141)は次のとおりです。

arr instanceof Array: false
arr instanceof w.Array: true
Array.isArray(arr): true
toString(arr): [object Array]
d instanceof Date: false
toString(d): [object Date]
err instanceof Error: false
Error.isError(err): true
new w.Object() instanceof Object: false
typeof arr: object

iframe側で作った配列は、親ページの Array に対するinstanceofがfalseになります。 一方で Array.isArray と Object.prototype.toString.call は、どの窓で作られた値でも正しく判定します。

窓をまたぐ値の判定方法の選び方

調べたい相手 窓をまたいでも使える書き方 補足
配列 Array.isArray(v) すべての主要ブラウザーで利用可能
エラー Error.isError(v) Chrome 134、Firefox 138、Safari 18.4 以降(MDNの対応データ @mdn/browser-compat-data 8.1.4 による)。Node.js v22.22.2 では未搭載
Date・Map・Set・RegExp など Object.prototype.toString.call(v) === '[object Date]' 組み込みの種類であれば区別できる
プリミティブ・関数 typeof v 窓に関係なく同じ結果

Error.isError は手元の Chromium 141 では true を返し、Node.js v22.22.2 では typeof Error.isError が "undefined" でした。Node.jsで使う場合は、実行するバージョンで使えるかを先に確かめると安心です。

Node.jsでも、node:vm モジュールで別のコンテキスト(別の実行環境)を作ると同じ現象を確認できます。

import vm from 'node:vm';
const other = vm.runInNewContext('[1,2,3]');
console.log(other instanceof Array); // false
console.log(Array.isArray(other));   // true

Array.isArrayで配列を判定する

配列の判定は Array.isArray(v) 一択です。typeofでは "object" になり、instanceofは窓をまたぐとfalseになるためです。

console.log(Array.isArray([]));                // true
console.log(Array.isArray([1, 2]));            // true
console.log(Array.isArray('abc'));             // false
console.log(Array.isArray({ length: 0 }));     // false(配列風オブジェクト)
console.log(Array.isArray(new Uint8Array(1))); // false(型付き配列)
console.log(ArrayBuffer.isView(new Uint8Array(1))); // true
値 Array.isArray 配列として扱いたいとき
[]、new Array(3) true そのまま使える
new Uint8Array(1) などの型付き配列 false ArrayBuffer.isView(v) で判定
arguments、NodeList false Array.from(v) で配列に変換
文字列 false [...v] で1文字ずつの配列に変換
new Set() false [...v] で配列に変換

「forで回せるか」を知りたいだけなら、typeof v?.[Symbol.iterator] === 'function' で反復可能(イテラブル)かどうかを調べる方法もあります。

Object.prototype.toStringで組み込みの種類を調べる

Object.prototype.toString.call(v) は [object Xxx] という文字列を返します。Xxxの部分が組み込みの種類名で、nullや配列、Date、Mapなどを1つの方法で区別できます。

const ts = v => Object.prototype.toString.call(v);

console.log(ts(null));           // [object Null]
console.log(ts([]));             // [object Array]
console.log(ts(new Date()));     // [object Date]
console.log(ts(new Map()));      // [object Map]
console.log(ts(async () => {})); // [object AsyncFunction]
console.log(ts(ts(1)).slice(8, -1)); // "String"(slice(8, -1) で種類名だけ取り出す)

.call(v) を付けるのは、[].toString() のように直接呼ぶと、配列や日付が自分用に上書きした別のtoStringが動くためです。Object.prototype にある元のtoStringを、vを対象にして呼び出しています。

自作クラスとSymbol.toStringTag

自作クラスのインスタンスは [object Object] になります。クラスに Symbol.toStringTag を定義すると、この表示を変えられます。

class User {}
class Tagged { get [Symbol.toStringTag]() { return 'Tagged'; } }

console.log(ts(new User()));   // [object Object]
console.log(ts(new Tagged())); // [object Tagged]
console.log(ts({ [Symbol.toStringTag]: 'Array' })); // [object Array]

3行目のとおり、タグは自由に付けられるので、ふつうのオブジェクトが [object Array] と名乗ることもできます。外から受け取った値が本当に配列かを確かめたいときは Array.isArray を使い、toStringは「組み込みの種類名を手軽に知るための道具」として使うと安全です。

nullかundefinedかを判定するコピペ用コード

「値が入っていない」状態を調べる書き方です。nullとundefinedをまとめて調べるなら v == null が最も短く書けます。

const isNull = v => v === null;
const isUndefined = v => v === undefined;
const isNullish = v => v == null; // null と undefined のどちらでも true

for (const v of [null, undefined, 0, '', false, NaN]) {
  console.log(String(v).padEnd(10), isNullish(v));
}
null       true
undefined  true
0          false
           false
false      false
NaN        false

==(ゆるい等価)は型を変換して比べる演算子ですが、nullとundefinedは互いにだけ等しく、0や空文字とは等しくならないと決まっています。実際に確かめた結果が次の表です。

式 結果
null == undefined true
null === undefined false
null == 0 false
null == "" false
null >= 0 true(大小比較ではnullが0に変換される)

判定の書き方の比較

書き方 null undefined 0 "" false NaN 向いている用途
v == null true true false false false false 値が入っていないかを調べる
v === null true false false false false false nullだけを調べる
v === undefined false true false false false false undefinedだけを調べる
!v true true true true true true 「偽とみなせる値」全般を調べる
v ?? '既定値' 既定値 既定値 0 "" false NaN 値がないときだけ既定値を使う
v || '既定値' 既定値 既定値 既定値 既定値 既定値 既定値 偽とみなせる値すべてを既定値にする

!v や || は0や空文字も拾うため、「0件」「空欄で入力された」を有効な値として扱いたいときは == null と ??(null合体演算子)を選びます。オブジェクトのプロパティを安全にたどるなら user?.address?.city のようにオプショナルチェーン(?.)を使うと、途中がnullやundefinedでもエラーにならずundefinedが返ります。

空かどうかを判定するコピペ用コード

「空」の意味は値の種類ごとに違います。文字列と配列はlength、MapとSetはsize、オブジェクトはキーの数で調べます。次の関数は、それらをまとめて判定します。

const isEmpty = v => {
  if (v == null) return true;                               // null / undefined
  if (typeof v === 'string' || Array.isArray(v)) return v.length === 0;
  if (v instanceof Map || v instanceof Set) return v.size === 0;
  if (typeof v === 'object') return Object.keys(v).length === 0;
  return false;                                             // 数値・真偽値などは空ではない
};

const cases = [null, '', ' ', [], [0], {}, { a: 1 }, new Map(), new Set([1]), 0, false];
for (const v of cases) console.log(JSON.stringify(v) ?? String(v), '->', isEmpty(v));

実行結果を表にまとめました。

値 isEmptyの結果 理由
null true 値が入っていない
"" true 長さ0の文字列
" "(空白1文字) false 長さ1の文字列
[] true 要素数0
[0] false 要素が1つある
{} true キーが0個
{a: 1} false キーが1個
new Map() true sizeが0
new Set([1]) false sizeが1
0 false 数値は空として扱わない
false false 真偽値は空として扱わない

空白だけの文字列も空とみなしたい場合は、文字列の行を v.trim().length === 0 に変えます。0やfalseを空とみなすかどうかは用途によって変わるので、チームで「空」の定義を1つに決めて関数にしておくと、判定のばらつきを防げます。

用途別の短い書き方も載せておきます。

調べたいこと 書き方
空文字か v === ''
空白だけの文字列か typeof v === 'string' && v.trim() === ''
空の配列か Array.isArray(v) && v.length === 0
空のオブジェクトか v !== null && typeof v === 'object' && Object.keys(v).length === 0
空のMap・Setか v.size === 0

数値として有効かを判定するコピペ用コード

数値の判定でつまずきやすいのは、NaNとInfinityもtypeofでは "number" になる点です。計算に使える数値かどうかは Number.isFinite を組み合わせて調べます。

// 計算に使える数値か(NaN・Infinity・文字列・BigInt は false)
const isValidNumber = v => typeof v === 'number' && Number.isFinite(v);

// 入力欄の文字列を数値に変換する(変換できなければ null)
const toValidNumber = s => {
  if (typeof s !== 'string' || s.trim() === '') return null;
  const n = Number(s);
  return Number.isFinite(n) ? n : null;
};
値 isValidNumber 値(文字列) toValidNumber
42 true "42" 42
-0.5 true " 3.5 " 3.5
NaN false "" null
Infinity false " " null
"42" false "12px" null
null false "1e3" 1000
10n false "0x10" 16

toValidNumber で空文字を先に除いているのは、Number("") が0になるためです。変換の結果は次のようになります。

式 結果
Number("") 0
Number(null) 0
Number(undefined) NaN
Number([]) 0
isNaN("abc") true(文字列を数値に変換してから判定)
Number.isNaN("abc") false(変換せずに判定)
isNaN("") false(空文字は0に変換される)

"1e3"(指数表記)や "0x10"(16進数)も数値に変換される点は、入力欄の仕様に合わせて調整してください。10進数の整数だけを受け付けたい場合は、/^-?\d+$/.test(s) のように正規表現で形を先に確かめる方法が向いています。

NaNの判定はNumber.isNaNを使う

NaNは「自分自身と等しくない」唯一の値です。=== では判定できないため、専用の方法を使います。

console.log(NaN === NaN);           // false
console.log(Number.isNaN(NaN));     // true
console.log(Object.is(NaN, NaN));   // true
console.log([NaN].includes(NaN));   // true
console.log([NaN].indexOf(NaN));    // -1(indexOfは === で比べるため見つからない)

無効な日付の判定にも同じ考え方が使えます。new Date('x') はエラーにならず「Invalid Date」というDateオブジェクトを返すので、Number.isNaN(d.getTime()) で確かめます(実行結果はtrueでした)。

整数・安全な整数・BigInt

調べたいこと 書き方 例
整数か Number.isInteger(v) Number.isInteger(5.0) はtrue
正確に表せる範囲の整数か Number.isSafeInteger(v) Number.isSafeInteger(2 ** 53) はfalse
BigIntか typeof v === 'bigint' typeof (1n + 2n) は "bigint"

BigIntとnumberは種類が別なので、混ぜて計算すると次のエラーになります。Number(big) か BigInt(num) でどちらかにそろえてから計算します。

TypeError: Cannot mix BigInt and other types, use explicit conversions

Symbolも、文字列と + でつなぐとエラーになります。文字列にしたいときは String(sym) か sym.description を使います。

TypeError: Cannot convert a Symbol value to a string

Promiseや関数・クラスを判定する

Promiseは「thenを持つか」で調べる

v instanceof Promise でも判定できますが、ライブラリーが独自に作ったPromise互換の値(thenable)はfalseになります。awaitできるかどうかを知りたいなら、thenメソッドの有無で調べます。

const isThenable = v => v != null && typeof v.then === 'function';

console.log(isThenable(Promise.resolve())); // true
console.log(isThenable({ then() {} }));     // true
console.log(isThenable(42));                // false

なお、await は相手がPromiseでなくてもそのまま値を返すので、「Promiseかもしれない値」は判定せずに await v と書くだけで済む場面も多くあります。

関数とクラスを見分ける

クラスもtypeofでは "function" です。見分けたいときは、関数のソース文字列が class で始まるかを調べる方法があります。

const isClass = v =>
  typeof v === 'function' && /^class\s/.test(Function.prototype.toString.call(v));

class User {}
console.log(isClass(User));        // true
console.log(isClass(() => {}));    // false
console.log(isClass(function () {})); // false

この方法は、ビルドツールでクラスを関数の形に変換している場合はfalseになります。変換後のコードでも動かしたいときは、「クラスかどうか」ではなく「呼び出せるかどうか(typeof v === 'function')」で設計するのが確実です。

汎用のgetType関数(コピペ用)

ここまでの内容を1つにまとめた関数です。nullは "null"、配列は "array"、NaNは "nan"、クラスは "class" と、typeofより細かい種類名を小文字で返します。

/**
 * 値の種類名を小文字の文字列で返す。
 * 例: 'null' 'undefined' 'string' 'number' 'nan' 'bigint' 'boolean' 'symbol'
 *     'array' 'object' 'date' 'regexp' 'map' 'set' 'promise' 'error'
 *     'function' 'class' 'uint8array' ...
 */
function getType(v) {
  if (v === null) return 'null';
  const t = typeof v;
  if (t !== 'object' && t !== 'function') {
    return t === 'number' && Number.isNaN(v) ? 'nan' : t; // プリミティブ
  }
  if (t === 'function') {
    return /^class\s/.test(Function.prototype.toString.call(v)) ? 'class' : 'function';
  }
  return Object.prototype.toString.call(v).slice(8, -1).toLowerCase(); // 組み込みの種類名
}

Node.js v22.22.2 での実行結果です。

値 getTypeの結果 値 getTypeの結果
null null new Date() date
undefined undefined /a/ regexp
42 number new Map() map
NaN nan new Set() set
10n bigint Promise.resolve() promise
"a" string new Error("e") error
true boolean () => {} function
Symbol() symbol async () => {} function
[] array class User {} class
{} object new User() object
new Uint8Array(1) uint8array 別コンテキストの配列 array

最後の「別コンテキストの配列」は、vm.runInNewContext('[1,2,3]') で作った配列です。toStringを使っているので、窓やコンテキストをまたいでも "array" と判定できます。

getTypeの使い方の例

種類ごとに処理を分けるときは、switch文と組み合わせると読みやすくなります。

function describe(v) {
  switch (getType(v)) {
    case 'null':
    case 'undefined': return '値なし';
    case 'nan':       return '数値として無効';
    case 'number':    return `数値 ${v}`;
    case 'string':    return `文字列(${v.length}文字)`;
    case 'array':     return `配列(${v.length}件)`;
    case 'date':      return `日付 ${v.toISOString()}`;
    case 'map':
    case 'set':       return `コレクション(${v.size}件)`;
    default:          return getType(v);
  }
}
console.log(describe([1, 2, 3])); // 配列(3件)
console.log(describe(NaN));       // 数値として無効

自作クラスのインスタンスは "object" を返します。クラス名まで知りたいときは v.constructor?.name を併用するか、クラスごとに v instanceof User で調べます。

TypeScriptで型判定の結果を型に反映する

TypeScriptでは、typeof・instanceof・Array.isArrayによる判定が型の絞り込み(ナローイング)として働きます。判定したブロックの中では、その型のメソッドが補完されます。以下は一般に知られたTypeScriptの仕様に沿ったコード例で、今回の検証では未実行です。

function size(v: string | number[] | Map<string, number> | null): number {
  if (v === null) return 0;                // ここから下は null ではない
  if (typeof v === 'string') return v.length;   // v は string
  if (Array.isArray(v)) return v.length;        // v は number[]
  return v.size;                                // v は Map<string, number>
}

// 自作の判定関数は「v is 型」と書くと絞り込みに使える
const isNullish = (v: unknown): v is null | undefined => v == null;

よくあるつまずきと直し方

書いたコード 起きること 直し方
typeof v === 'null' 常にfalse(typeofは "null" を返さない) v === null
typeof v === 'array' 常にfalse(typeofは "array" を返さない) Array.isArray(v)
typeof v === 'object' でオブジェクト判定 nullと配列もtrueになる v !== null && typeof v === 'object' && !Array.isArray(v)
v === NaN 常にfalse Number.isNaN(v)
isNaN(v) で数値チェック 空文字やnullが0に変換されてfalseになる typeof v === 'number' && Number.isFinite(v)
'a' instanceof String false(プリミティブ) typeof v === 'string'
arr instanceof Array(iframe経由の値) false Array.isArray(arr)
if (!v) で未入力チェック 0やfalseも未入力扱いになる v == null または v === ''
typeof v == 'String' 常にfalse(結果は小文字) 'string' と小文字で書く
v.constructor === Object でふつうのオブジェクト判定 Object.create(null) で作った値はfalseになる(constructorがundefined) プロトタイプなしも含めたいなら Object.getPrototypeOf(v) === null も条件に加える

よくある質問

Q. typeofとinstanceofはどう使い分けますか? A. 文字列・数値・真偽値・BigInt・Symbol・undefined・関数といったプリミティブ寄りの判定はtypeof、DateやMap、自作クラスのインスタンスのようなオブジェクトの判定はinstanceofを使います。配列だけは Array.isArray、nullだけは === null と覚えると整理できます。

Q. nullとundefinedをまとめて判定する書き方は? A. v == null です。== を使うと、nullとundefinedの両方でtrue、0や空文字ではfalseになります。ESLintの eqeqeq ルールを使っている場合は、null との比較だけ許可する設定("eqeqeq": ["error", "always", { "null": "ignore" }])にするか、v === null || v === undefined と書きます。

Q. Object.prototype.toString.callはいつ使いますか? A. DateやRegExp、Mapなどの組み込みオブジェクトを、iframeなど別の窓で作られた値も含めて区別したいときに使います。getTypeのような汎用関数の中で使うのが定番です。自作クラスは [object Object] になるので、クラスの判定にはinstanceofを選びます。

Q. 「空のオブジェクトか」を調べる一番短い書き方は? A. Object.keys(v).length === 0 です。vがnullやundefinedだとエラーになるため、v != null && を前に付けるか、この記事の isEmpty 関数のように先にnullを除いてから調べます。

まとめ

  • プリミティブと関数はtypeof。返る文字列は "undefined" "boolean" "number" "bigint" "string" "symbol" "function" "object" の8種類。
  • nullは v === null、nullとundefinedをまとめるなら v == null。typeof nullは "object" になる。
  • 配列は Array.isArray(v)。typeofでは "object"、instanceofは窓をまたぐとfalseになる。
  • 自作クラスやDate・Mapはinstanceof。別の窓の値も見るなら Object.prototype.toString.call(v) や Error.isError(v) を使う。
  • 計算に使える数値かは typeof v === 'number' && Number.isFinite(v)。NaNの判定は Number.isNaN(v)。
  • 「空」の定義は種類ごとに違う。length・size・キーの数をまとめた isEmpty 関数を用意しておくと判定がそろう。
  • 種類名を細かく知りたいときはgetType関数。null・array・nan・classまで区別できる。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript型判定 typeof・instanceof・null早見表」

株式会社RJCでは、JavaScriptの型判定のような基礎の仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?