cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Checking types reliably

Choose the right JavaScript type check for null, arrays, class instances, object labels, and errors without being fooled by old quirks or cross-realm values.

By the end, you can
  • 01
    Read quick checks correctlyExplain why typeof null is object and wrap it safely.
  • 02
    Pick the right inspectorUse instanceof, Array.isArray, and object tags for their best jobs.
  • 03
    Handle errors honestlyFeature-detect Error.isError and know the fallback limits.

The reliable-check mindset

INTERACTIVE

JavaScript lets values travel far: from form fields, JSON, libraries, iframes, tests, and your own classes. By the time a value reaches your function, you often need to ask a small question before touching it: is this really an array? Did someone pass null? Is this thrown value an Error or just a random object?

The trick is that JavaScript does not have one perfect type-check button. It has several inspectors, and each answers a different question. typeof is a fast glance. instanceof checks a family tree. Array.isArray is a specialist. The Object.prototype.toString label reader is useful but can be customized. Error.isError is the new error specialist, but it is not everywhere yet.

Real-life analogyReading a parcel label

A parcel label gives a quick clue about what is inside. Some checks are stronger than others, so use the one that answers your question.

In real life: Reading the parcel label
In JavaScript: typeof: fast, broad labels
In real life: Checking who sent the parcel
In JavaScript: instanceof: is this prototype in the chain?
In real life: Checking for the array mark
In JavaScript: Array.isArray: arrays across realms
In real life: Reading the printed label
In JavaScript: Object.prototype.toString.call(value)

Where the analogy stops: Parcel labels can be wrong. In JavaScript, Symbol.toStringTag can customize labels, so a label is not proof.

Start with a real matrix. Read across one row to compare checks for a single value. Read down one column to see what a check is good at. The Error.isError column is feature-detected in your browser after mount, because this new API is missing in Node 22 and in many browsers.

Type-check matrix
Matrix sourcePop out in the code editor (opens in a new tab)JavaScript
const values = [null, [], new Date(), new Error("boom")];for (const value of values) {  console.log(typeof value);  console.log(value instanceof Error);  console.log(Array.isArray(value));  console.log(Object.prototype.toString.call(value));}
Runtime results15 rows
Valuetypeofinstanceof Objectinstanceof Arrayinstanceof ErrorArray.isArrayObject tagError.isError
nullobjectfalsefalsefalsefalse[object Null]unavailable
undefinedundefinedfalsefalsefalsefalse[object Undefined]unavailable
[]objecttruetruefalsetrue[object Array]unavailable
{}objecttruefalsefalsefalse[object Object]unavailable
new Date()objecttruefalsefalsefalse[object Date]unavailable
/re/objecttruefalsefalsefalse[object RegExp]unavailable
new Map()objecttruefalsefalsefalse[object Map]unavailable
function() {}functiontruefalsefalsefalse[object Function]unavailable
class Demo {}functiontruefalsefalsefalse[object Function]unavailable
new Error('boom')objecttruefalsetruefalse[object Error]unavailable
Promise.resolve(1)objecttruefalsefalsefalse[object Promise]unavailable
5nbigintfalsefalsefalsefalse[object BigInt]unavailable
Symbol()symbolfalsefalsefalsefalse[object Symbol]unavailable
NaNnumberfalsefalsefalsefalse[object Number]unavailable
{ [Symbol.toStringTag]: 'Custom' }objecttruefalsefalsefalse[object Custom]unavailable
  • nullOnly strict equality or a helper should call this null.
  • undefinedThe only value whose typeof is undefined.
  • []Arrays are objects, but Array.isArray is the trusted check.
  • {}Plain objects need shape checks, not just labels.
Try it yourself

Checking for Error.isError after mount…

Real JavaScript results for common values. Error.isError is feature-detected only in the browser after hydration.

typeof null: the famous old quirk

STEP THROUGH

You met the typeof table in Data types and saw the operator again in The rest of the operators. Most of its answers are handy: "string", "number", "boolean", "bigint", "symbol", "undefined", "function", or "object". The historic surprise is typeof null.

In early engines, values were represented with small type tags. Object values used one tag, and the null pointer ended up looking like that object tag. The exact engine details are old history, but the result became web-compatible behavior: typeof null still returns "object". It does not mean null has properties or behaves like an object.

Step through the typeof null quirk
Step 0 of 5Ready
Your turn: follow the blue line

Predict the four-line helper result before stepping through the real run.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const quickCheck = typeof value;const exactCheck = value === null;const type = getType(value);console.log(quickCheck, exactCheck, type);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Safe null checks

Use value === null when you specifically mean null. If your code intentionally accepts both null and undefined as “missing,” you may see value == null, but that is a broader missing-value check, not an exact null check.

A small helper can make display labels friendlier without pretending JavaScript changed its rules:

Friendly type helperPop out in the code editor (opens in a new tab)JavaScript
function getType(value) {
  if (value === null) return "null";
  if (Array.isArray(value)) return "array";
  return typeof value;
}

The order is the lesson: check null before raw typeof, and check arrays before calling every object an object.

instanceof: a family-tree check

STEP THROUGH

For this lesson, the precise mental model is one sentence: object instanceof Constructor asks whether Constructor.prototype is somewhere in the object’s prototype chain. The prototype chain itself is the next stage’s topic; here, you only need that one relationship.

Real-life analogyA family tree you can check

instanceof checks whether a family name appears in an object’s tree. An iframe or test VM has its own tree, so matching names can still come from different constructors.

In real life: A person’s family tree
In JavaScript: An object’s prototype chain
In real life: A family name in that tree
In JavaScript: Constructor.prototype
In real life: Adoption paperwork changing the tree
In JavaScript: Object.setPrototypeOf changing results
In real life: Another family’s tree
In JavaScript: Another realm with its own constructors

Where the analogy stops: Objects are not people: JavaScript can change an object's prototype chain, and later lessons cover the deeper mechanics.

Step through this example twice. First keep the chain. Then switch line 4 so the object’s prototype is replaced with null, and predict whether the second check can change.

Change the family tree
Step 0 of 6Ready
Your turn: follow the blue line

Switch line 4, predict the second instanceof, then step through the real result.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const basket = new Basket();const before = basket instanceof Basket;// keep Basket.prototype in the chainconst after = basket instanceof Basket;console.log(before, after);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Choose what line 4 does before the second check.

Changing line 4 starts a fresh replay. Predict the value of after.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

instanceof has three practical pitfalls:

  • Primitives are not wrapper objects: "x" instanceof String is false. Use typeof value === "string" for string primitives.
  • Cross-realm values have different constructors. An array or error from an iframe can fail your page’s instanceof Array or instanceof Error check.
  • Prototype changes affect the result. That is uncommon in everyday application code, but it proves what the operator reads.

Array.isArray: the trusted array inspector

Arrays are objects, so typeof [] returns "object". They are often created by the same realm as your code, so [] instanceof Array usually returns true in quick experiments. But “usually” is not a reliable API boundary.

Use Array.isArray(value) when the real question is “is this a JavaScript Array?” It recognizes arrays from other realms, including iframe-like environments, and it rejects plain objects that merely have 0 and length properties.

Cross-realm proof in the tests

The lesson’s regression tests use node:vm contexts to prove the cross-realm behavior: an array from another context fails value instanceof Array in the current context, but Array.isArray(value) returns true. The same tests show the parallel problem for errors.

The common review comment is simple: if you see typeof value === "object" or value instanceof Array guarding array methods, ask for Array.isArray(value) instead.

Object.prototype.toString and Symbol.toStringTag

STEP THROUGH

Every object inherits a toString method, but calling a value’s own toString is not consistent: arrays join their items, dates format dates, and custom objects can override the method. The classic label reader is the original method, borrowed directly:

Borrow the original label readerPop out in the code editor (opens in a new tab)JavaScript
Object.prototype.toString.call(value)

That call returns labels such as [object Array], [object Null], [object Date], [object Map], and [object Promise]. The Symbols lesson previewed well-known symbols; here you see one in action. Symbol.toStringTag lets built-ins and your own objects customize the label.

Read object labels
Step 0 of 7Ready
Your turn: follow the blue line

Step through the labels, then ask whether a label is proof or just a useful clue.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const arrayTag = tag.call([]);const nullTag = tag.call(null);const mapTag = tag.call(new Map());const custom = { [Symbol.toStringTag]: "Custom" };const customTag = tag.call(custom);console.log(arrayTag, nullTag, mapTag, customTag);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
A label, not a badge scanner

Because Symbol.toStringTag is customizable, an object can claim a friendly label. That is useful for display and debugging, but not proof that the object has the behavior of an array, map, or error. Prefer dedicated checks when they exist.

Error.isError: new, useful, and not everywhere

INTERACTIVE

Error detection has the same cross-realm problem as arrays. An error created in another iframe or VM context inherits from that realm’s Error.prototype, not yours. That means err instanceof Error can be false even for a genuine error.

Error.isError(value) is a very recent addition designed to answer “is this really an Error object?” more reliably, including across realms and against objects that merely fake name and message. It is not in Node 22 and is missing in many browsers, so client code must feature-detect it before calling it.

Feature-detect Error.isError
Error helperPop out in the code editor (opens in a new tab)JavaScript
function isErrorReliable(value) {  if (typeof Error.isError === "function") {    return Error.isError(value);  }  return Object.prototype.toString.call(value) === "[object Error]";}
Selected samplereal
Object tag[object Error]
Fallback resulttrue
Error.isError resultunavailable
Try it yourself

Waiting until after mount avoids server/browser feature-detection mismatches.

The fallback is useful for older runtimes, but a custom Symbol.toStringTag can fool a tag-based check.

Until the API is broadly available, a common fallback is:

Fallback error helperPop out in the code editor (opens in a new tab)JavaScript
function isErrorFallback(value) {
  return Object.prototype.toString.call(value) === "[object Error]";
}

That fallback is useful because real cross-realm errors commonly keep the [object Error] label. But it is limited: labels can be customized with Symbol.toStringTag, so do not treat it as an unfakeable security boundary.

Which check should you use?

INTERACTIVE

In real code, the right check follows from the job. A validator, a parser, and an error reporter do not need the same answer. This table is the everyday cheat sheet.

Which check to use
GoalUseWhy
Exactly nullvalue === nullAvoid the typeof null quirk and avoid matching undefined by accident.
ArraysArray.isArray(value)Works across realms and rejects array-like plain objects.
ErrorsError.isError(value) when available; fallback tag with limitsDesigned for cross-realm Error detection; fallback is useful but not perfect.
Dates in your realmvalue instanceof Date plus validity checksChecks the constructor relationship; also check !Number.isNaN(value.getTime()) for a valid date.
Plain objectsvalue !== null && typeof value === "object" && !Array.isArray(value)Start by excluding null and arrays; add prototype checks if you require literals only.
Functionstypeof value === "function"Functions get a special typeof result.
Your class instancesvalue instanceof YourClassClear when the value should come from your constructor in the same realm.

Now sort each situation by the check that most directly answers it.

Pick the right type check
  • Is this value a string primitive?
  • Is this callable value a function?
  • Was this Date created by this page's Date constructor?
  • Was this object made by your Invoice class?
  • Is this value an Array, even if it came from an iframe?
  • Should I avoid calling an array a plain object?
  • What built-in label does null have?
  • Do I want a display label such as [object Map]?
Try it yourself
0 of 8 correct

Sort each scenario by the check that answers the question with the fewest blind spots.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

“typeof null means null is an object.”

It means JavaScript preserves a legacy result. null has no properties and should be checked with value === null.

“typeof [] should be enough for arrays.”

Arrays are objects, so raw typeof cannot distinguish them. Use Array.isArray.

“instanceof proves where a value came from forever.”

It reads the current prototype chain. Cross realms and prototype changes can affect the answer.

“Object.prototype.toString cannot lie.”

It reads a label, and Symbol.toStringTag can customize that label. A label is useful, not absolute proof.

“A thrown thing is always an Error.”

JavaScript lets code throw any value. Error detection helps you decide how to report it, but your catch blocks should still handle strange thrown values gracefully.

Practice: choose the right check

5 EXERCISES
Exercise 1 · Warm-upPredict three checks

Predict the three console lines without running the code first.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log(typeof null);
console.log(null === null);
console.log(Array.isArray([]));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeWrite a friendly getType helper

    Write the helper in your console or editor. It should return "null" for null, "array" for arrays, and otherwise return JavaScript’s typeof result.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function getType(value) {
      if (value === null) return "null";
      if (Array.isArray(value)) return "array";
      return typeof value;
    }
    console.log(getType(null));
    console.log(getType([]));
    console.log(getType(5n));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFix the array guard

      This function tries to avoid non-arrays, but its guard is impossible. What check should replace it?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function firstItem(value) {
        if (typeof value !== "array") return "not an array";
        return value[0];
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeDetect errors with an honest fallback

        Before Error.isError is available everywhere, write a fallback using the object tag and state its limitation.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function isErrorFallback(value) {
          return Object.prototype.toString.call(value) === "[object Error]";
        }
        console.log(isErrorFallback(new Error("boom")));
        console.log(isErrorFallback({ name: "Error", message: "fake" }));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain the cross-realm problem

          In one sentence, explain why an array or error from another realm can fail an instanceof check in your realm.

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Your first try counts, but every answer explains the reasoning. Look for the question being asked before picking the operator.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does typeof null return?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(typeof null);

              Choose an answer to see the explanation.

            2. Question 2 of 7Which check is the reliable way to detect arrays, including arrays from another realm?

              Choose an answer to see the explanation.

            3. Question 3 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("x" instanceof String);

              Choose an answer to see the explanation.

            4. Question 4 of 7What is instanceof checking in the ordinary case?

              Choose an answer to see the explanation.

            5. Question 5 of 7What label does this custom tag print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const custom = { [Symbol.toStringTag]: "Array" };
              console.log(Object.prototype.toString.call(custom));

              Choose an answer to see the explanation.

            6. Question 6 of 7When Error.isError is missing, what should a lesson helper honestly say about the fallback?

              Choose an answer to see the explanation.

            7. Question 7 of 7What does this getType helper print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function getType(value) {
                if (value === null) return "null";
                if (Array.isArray(value)) return "array";
                return typeof value;
              }
              console.log(getType([]));

              Choose an answer to see the explanation.

            Key takeaways

            • typeof is best for primitive labels and functions; it famously returns "object" for null.
            • Use value === null for null and Array.isArray(value) for arrays.
            • instanceof asks whether a constructor’s prototype is in an object’s prototype chain, so realms and prototype changes matter.
            • Object.prototype.toString.call(value) reads useful labels, but Symbol.toStringTag means labels can be customized.
            • Feature-detect Error.isError. Use a tag fallback only with its limits clearly understood.

            One-liner.
            A reliable type check asks the smallest exact question: null, array, prototype relationship, object label, or real error.

            Up next: The prototype chain, where the family tree behind property lookup and instanceof gets its full explanation.

            CompleteFrontend Clear concepts. Working examples.