- JavaScript
JavaScript型判定 typeof・instanceof・null早見表
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だけでは見分けがつかない組み合わせがはっきりします。
- nullはtypeofで "object"。
v === nullで調べます。toStringなら[object Null]と区別できます。 - 配列はtypeofで "object"。
Array.isArray(v)だけがtrueを返します。 - NaNとInfinityはtypeofで "number"。toStringでも
[object Number]なので、Number.isNaNとNumber.isFiniteで調べます。 - クラスはtypeofで "function"。クラスは関数の一種として作られているためです。
- 自作クラスのインスタンスはtoStringで
[object Object]。クラス名までは出ないので、instanceofを使います。 new Number(1)のようなラッパーオブジェクトはtypeofで "object"。toStringでは[object Number]になり、プリミティブの42と同じ表示になります。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期連続の黒字決算。