JavaScripttypeof & instanceof

typeof and Other Type Checks

Type-checking sounds like a solved problem until you try it in JavaScript. typeof is fast and built-in, but it has well-known quirks. instanceof solves a different problem and has its own edge cases. Arrays, dates, maps and other "kinds of object" each have their own dedicated test. This page is the practical guide to "what is this value, really?"

typeof — the fast check

typeof value returns a lowercase string describing the type. There are exactly eight possible return values.

JS
typeof "hello";        // "string"
typeof 42;             // "number"
typeof true;           // "boolean"
typeof undefined;      // "undefined"
typeof 10n;            // "bigint"
typeof Symbol("id");   // "symbol"
typeof function(){};   // "function"
typeof {};             // "object"

// Less obvious:
typeof null;           // "object"   <- famous quirk
typeof [];             // "object"
typeof new Date();     // "object"
typeof NaN;            // "number"   <- NaN is a numeric value
The famous quirks
`typeof null === 'object'`
This is a bug in the very first JavaScript implementation, preserved for backward compatibility — fixing it would break too much code. To detect null, compare directly: `value === null`. To distinguish null from a real object, combine both checks.

JS
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

isPlainObject({ a: 1 });   // true
isPlainObject([1, 2]);     // false
isPlainObject(null);       // false
isPlainObject(new Date()); // true   — Date is technically an object
                           // a stricter version would also check the prototype

Other small surprises: typeof function(){} is "function" (functions are the one object subtype with its own label), and typeof undeclaredName is "undefined"typeof is the only operator that doesn't throw when the operand doesn't exist.

JS
// console.log(zzz);              // ReferenceError
console.log(typeof zzz);          // "undefined"  — safe
instanceof — checking the prototype chain

value instanceof Constructor returns true if Constructor.prototype appears anywhere in the prototype chain of value.

JS
[] instanceof Array;          // true
[] instanceof Object;         // true — Array extends Object
new Date() instanceof Date;   // true
/^a/ instanceof RegExp;       // true

class Animal {}
class Dog extends Animal {}
const d = new Dog();
d instanceof Dog;             // true
d instanceof Animal;          // true
d instanceof Object;          // true
instanceof doesn't work across realms
In a browser, each `<iframe>` has its own `Array` constructor. An array from another frame is not `instanceof Array` of the current frame. That's why `Array.isArray()` exists — it works correctly across realms.
Array.isArray — the definitive array test

JS
Array.isArray([]);            // true
Array.isArray([1, 2, 3]);     // true
Array.isArray("not an array");// false
Array.isArray({ length: 0 }); // false  — array-like ≠ array
Array.isArray(new Array(3));  // true

Use Array.isArray whenever you need to distinguish arrays from plain objects. typeof reports both as "object" and instanceof Array is unreliable across realms.

Other specific tests

JS
// Numbers
Number.isFinite(42);        // true
Number.isFinite(Infinity);  // false
Number.isFinite("42");      // false  — strict: no coercion
Number.isInteger(3);        // true
Number.isInteger(3.0);      // true   — exact float that happens to be whole
Number.isInteger(3.1);      // false
Number.isNaN(NaN);          // true
Number.isNaN("oops");       // false  — strict: only matches NaN

// Strings
typeof s === "string";      // for primitives
s instanceof String;        // only true for new String(""), avoid that anyway

// Promises (duck typing — has a .then)
typeof value?.then === "function";

// Plain object — strict version
function isObject(v) {
  return v !== null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
}
Putting it together

A small type classifier

JS
function classify(value) {
  if (value === null)            return "null";
  if (Array.isArray(value))      return "array";
  if (value instanceof Date)     return "date";
  if (value instanceof RegExp)   return "regex";
  if (value instanceof Map)      return "map";
  if (value instanceof Set)      return "set";
  return typeof value;
}

console.log(classify(null));      // "null"
console.log(classify([1, 2]));    // "array"
console.log(classify(new Date()));// "date"
console.log(classify(42));        // "number"
console.log(classify("hi"));      // "string"
console.log(classify({ a: 1 }));  // "object"
null
array
date
number
string
object
The toString tag — the heavy-duty option

Every object inherits Object.prototype.toString, which returns a string like "[object Date]" reflecting the value's internal class. Borrowing this method gives you a robust type tag that works for built-ins.

JS
Object.prototype.toString.call([]);          // "[object Array]"
Object.prototype.toString.call(new Date());  // "[object Date]"
Object.prototype.toString.call(/^a/);        // "[object RegExp]"
Object.prototype.toString.call(null);        // "[object Null]"
Object.prototype.toString.call(undefined);   // "[object Undefined]"
Object.prototype.toString.call(42);          // "[object Number]"

This isn't pretty, but it's bullet-proof against the typeof null quirk and cross-realm issues. You'll see it in libraries that need to reliably detect types.

Duck typing

Sometimes the right question isn't "what type is this?" but "does it have the methods I need?" That's duck typing, and it's idiomatic in JavaScript.

JS
function isThenable(value) {
  return value != null && typeof value.then === "function";
}

isThenable(Promise.resolve());           // true
isThenable({ then: () => {} });          // true — close enough to a Promise for await
isThenable("not a promise");             // false
Why duck typing wins
Native promises, library promises and `{ then }` objects all walk and talk like promises. Code that accepts "any thenable" is more flexible than code that demands `instanceof Promise`. The same logic applies to iterables (`Symbol.iterator`), error-likes (`.message` and `.stack`), and so on.
What TypeScript adds

TypeScript catches type errors at compile time, but at runtime your code still runs as plain JavaScript and still needs the same checks for any data crossing a trust boundary — user input, API responses, JSON.parse. Tools like Zod, Yup or Valibot let you validate the shape of data at runtime and infer TypeScript types from the same schema.

Pick the lightest tool that works
For "is this a number?" — `typeof`. For "is this an array?" — `Array.isArray`. For "is this a Date / Map / RegExp?" — `instanceof`. For "is this a plain object?" — combine `typeof === "object"`, `!== null` and `!Array.isArray`. For "does it quack?" — duck-type. Reserve `Object.prototype.toString.call(value)` for library code where you actually need it.