How to check a variable's data type
The Short Answer
JavaScript provides several ways to check a variable's type: typeof for primitives, instanceof for objects/class instances, Array.isArray() for arrays, and Object.prototype.toString.call() for the most reliable type detection. The tricky part is that typeof has quirks — it returns 'object' for null and arrays, which trips up developers who rely on it exclusively.
typeof — The Basic Check
typeof is the most common type-checking operator. It returns a string indicating the type of the operand. It works well for primitives (string, number, boolean, undefined, symbol, bigint) and functions, but has well-known quirks with null and objects.
// typeof returns a lowercase string
typeof 'hello' // 'string'
typeof 42 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof 10n // 'bigint'
typeof function(){} // 'function'
typeof {} // 'object'
typeof [] // 'object' ← not 'array'!
typeof null // 'object' ← famous bug, never fixed
typeof NaN // 'number' ← NaN is technically a number
typeof new Date() // 'object'
typeof /regex/ // 'object'
// typeof is safe on undeclared variables (doesn't throw)
typeof undeclaredVar // 'undefined' (no ReferenceError)
// vs direct access:
// undeclaredVar // ReferenceError: undeclaredVar is not defined
The typeof null === 'object' bug dates back to the first JavaScript implementation — null was represented with the same internal tag as objects. It was never fixed because too much existing code depends on this behavior. Always check for null explicitly before using typeof for object detection.
instanceof — Checking the Prototype Chain
instanceof checks whether an object's prototype chain contains the prototype property of a constructor. It's useful for checking if something is an instance of a specific class or built-in type. Unlike typeof, it can distinguish between different object types (Array vs Date vs RegExp).
// instanceof checks the prototype chain
[] instanceof Array // true
[] instanceof Object // true (arrays inherit from Object)
new Date() instanceof Date // true
/regex/ instanceof RegExp // true
// Custom classes
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
dog instanceof Dog // true
dog instanceof Animal // true (checks entire prototype chain)
dog instanceof Object // true
// ⚠️ Limitations of instanceof:
// 1. Doesn't work with primitives
'hello' instanceof String // false! (it's a primitive, not a String object)
42 instanceof Number // false!
true instanceof Boolean // false!
// 2. Breaks across iframes/realms (different global contexts)
// An array from an iframe: iframeArray instanceof Array → false
// Because it's a different Array constructor
// 3. Can be spoofed with Symbol.hasInstance
class EvenNumber {
static [Symbol.hasInstance](num) {
return typeof num === 'number' && num % 2 === 0;
}
}
4 instanceof EvenNumber // true
3 instanceof EvenNumber // false
Array.isArray() — The Reliable Array Check
Since typeof [] returns 'object' and instanceof Array breaks across iframes, JavaScript provides Array.isArray() as the definitive way to check if something is an array. It works across realms and is the only method you should use for array detection.
// Array.isArray — the only reliable array check
Array.isArray([]) // true
Array.isArray([1, 2, 3]) // true
Array.isArray(new Array()) // true
Array.isArray('hello') // false
Array.isArray({ length: 3 }) // false (array-like, but not an array)
// Why not use other methods?
typeof [] === 'array' // false! typeof returns 'object'
[] instanceof Array // true, but breaks across iframes
// Array.isArray works across iframes/realms
// const iframeArray = iframe.contentWindow.eval('[]');
// Array.isArray(iframeArray) → true ✅
// iframeArray instanceof Array → false ❌
Object.prototype.toString.call() — The Nuclear Option
For the most precise type detection, Object.prototype.toString.call() returns an internal [[Class]] tag like [object Array], [object Date], [object Null], etc. It correctly identifies null, arrays, dates, regex, and every other built-in type. It's verbose but never lies.
// Object.prototype.toString.call() — most precise type detection
Object.prototype.toString.call('hello') // '[object String]'
Object.prototype.toString.call(42) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(null) // '[object Null]' ← correct!
Object.prototype.toString.call([]) // '[object Array]' ← correct!
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(/regex/) // '[object RegExp]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(function(){}) // '[object Function]'
// Helper function to extract the type
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
getType(null) // 'null'
getType([]) // 'array'
getType(new Date()) // 'date'
getType(42) // 'number'
This method works because every object has an internal [[Class]] property that toString reads. By calling it with .call(), you can check any value — even primitives (which get temporarily wrapped). It's the most reliable but also the most verbose approach.
Practical Type-Checking Patterns
In real code, you'll combine these techniques based on what you need to check. Here are the recommended patterns for common type checks — each uses the most appropriate and reliable method for that specific type.
// Recommended type checks for each type:
// Primitives — use typeof
const isString = (v) => typeof v === 'string';
const isNumber = (v) => typeof v === 'number' && !Number.isNaN(v);
const isBoolean = (v) => typeof v === 'boolean';
const isUndefined = (v) => typeof v === 'undefined';
const isSymbol = (v) => typeof v === 'symbol';
const isBigInt = (v) => typeof v === 'bigint';
const isFunction = (v) => typeof v === 'function';
// Null — explicit check (typeof lies)
const isNull = (v) => v === null;
// Arrays — Array.isArray (only reliable method)
const isArray = (v) => Array.isArray(v);
// Plain objects — typeof + null check + not array
const isPlainObject = (v) =>
typeof v === 'object' && v !== null && !Array.isArray(v);
// NaN — Number.isNaN (not global isNaN which coerces)
const isNaN = (v) => Number.isNaN(v);
// Specific object types — instanceof or toString
const isDate = (v) => v instanceof Date;
const isRegExp = (v) => v instanceof RegExp;
const isMap = (v) => v instanceof Map;
const isSet = (v) => v instanceof Set;
const isPromise = (v) => v instanceof Promise;
// Or for cross-realm safety:
const isDateSafe = (v) =>
Object.prototype.toString.call(v) === '[object Date]';
Why Interviewers Ask This
This question tests whether you know JavaScript's type system quirks. Interviewers want to see that you know typeof null is 'object' (and why), understand that typeof can't distinguish arrays from objects, know when to use instanceof vs typeof vs Array.isArray(), can write robust type-checking code that handles edge cases, and understand the cross-realm problem with instanceof.
Quick Revision Cheat Sheet
Primitives: typeof — works for string, number, boolean, undefined, symbol, bigint
null: v === null (typeof returns 'object' — a known bug)
Arrays: Array.isArray() — the only reliable method
Class instances: instanceof — checks prototype chain
Any type (precise): Object.prototype.toString.call(v) — never lies
NaN: Number.isNaN(v) — not global isNaN() which coerces