Topics

Jump to a topic.

Search questions

Find a question by title, tag, or summary.

How do you reliably check a value's type?

Questions in this chapter (7)
  • Draft
  • medium
  • Askedvery often
  • Verified October 2026

Answer

Pick the check by what you're asking:

Question Check
Is it a string, number, boolean, bigint, symbol, or undefined? typeof value === '…'
Is it a function? typeof value === 'function'
Is it null? value === null (typeof null is "object")
Is it an array? Array.isArray(value)
Is it an error? Error.isError(value) where available
Is it an instance of my class? value instanceof MyClass
Is it NaN? Number.isNaN(value) (not the global isNaN, which coerces)
Which built-in kind is it? Object.prototype.toString.call(value)

Why the simple tools fail:

  • typeof knows eight results only. Arrays, dates, regexes and null are all "object".
  • instanceof checks whether Constructor.prototype is on the value's prototype chain (or calls Constructor[Symbol.hasInstance]). It's false for primitives ('s' instanceof String is false), true for any object that merely inherits from the prototype, and breaks across realms (iframes, vm contexts), because each realm has its own Array and Error.
  • Array.isArray and Error.isError are branded checks: they look at internal slots (an Array exotic object; [[ErrorData]]), not the prototype chain. They work across realms and aren't fooled by { __proto__: Array.prototype }. Array.isArray also sees through a Proxy to its target. Error.isError is newer and MDN lists it as not Baseline, so feature-detect it or use a polyfill.
  • Object.prototype.toString.call(v) returns [object Tag]: Null and Undefined for those values; otherwise the value of the object's Symbol.toStringTag property if that's a string, else a built-in tag (Array, Arguments, Function, Error, Boolean, Number, String, Date, RegExp) or Object. The spec says this isn't a reliable test for other objects, since programs can set Symbol.toStringTag themselves.

Example

const tagOf = (value) => Object.prototype.toString.call(value).slice(8, -1);
console.log(tagOf(null), tagOf([]), tagOf(new Date()), tagOf(new Map()), tagOf(1));
// → Null Array Date Map Number

const fakeArray = { __proto__: Array.prototype };
console.log(Array.isArray(fakeArray), fakeArray instanceof Array); // → false true

const fakeError = { __proto__: Error.prototype };
console.log(Error.isError(new TypeError('x')), Error.isError(fakeError)); // → true false

class Money {
  get [Symbol.toStringTag]() {
    return 'Money';
  }
}
console.log(tagOf(new Money())); // → Money (the tag is a program-controlled label)

const vm = await import('node:vm');
const foreign = vm.runInNewContext('[]'); // an array from another realm
console.log(foreign instanceof Array, Array.isArray(foreign)); // → false true

console.log('s' instanceof String, Number.isNaN('x'), isNaN('x')); // → false false true

Pitfalls

  • typeof x === 'object' as an "is it an object" test. It's true for null, so guard with x !== null, and it's false for functions.
  • x instanceof Array or x instanceof Error in code that may receive values from another realm (iframes, Node's vm). Use Array.isArray and Error.isError.
  • Trusting Object.prototype.toString for your own types. Any class can define Symbol.toStringTag to return what it likes.
  • Using isNaN(x) to detect NaN. The global function coerces first, so isNaN('x') is true; Number.isNaN does not coerce.

Follow-ups

How do I check for a plain object ({})?
There's no built-in. A common check is value !== null && typeof value === 'object' followed by comparing Object.getPrototypeOf(value) with Object.prototype or null. Decide whether class instances should count, then write that rule down.
Can instanceof be customised?
Yes. If the right-hand side has a Symbol.hasInstance method, instanceof calls it and uses its result; 1 instanceof A can return true if A[Symbol.hasInstance] says so.

Exercises

Predict the output

const arrayLike = { __proto__: Array.prototype, length: 0 };
const tag = Object.prototype.toString.call(arrayLike);
console.log(Array.isArray(arrayLike), arrayLike instanceof Array, tag);