Data types and type coercion — the == vs === story

JavaScript's automatic type coercion isn't random — it follows a specific, learnable algorithm — but the practical answer for almost every real comparison is simply to avoid triggering it at all, by using === instead of ==.

Beginner

3 min read

The seven primitive types, and everything else is an object

typeof "hello";     // "string"
typeof 42;           // "number"
typeof true;          // "boolean"
typeof undefined;     // "undefined"
typeof null;          // "object" — a famous, longstanding language bug, kept for backward compatibility
typeof Symbol();       // "symbol"
typeof 10n;            // "bigint"
typeof {};              // "object" — arrays, functions, and plain objects are all "object" (functions report "function")

JavaScript has exactly seven primitive types and one composite category (object, which arrays, functions, and plain objects all fall under, with typeof reporting "function" specifically for callable objects). typeof null returning "object" is a genuine, acknowledged historical bug in the language's very first implementation — it's never been fixed, because fixing it would break an enormous amount of existing code that depends on the (wrong) behavior, a real example of how early language design mistakes can become permanent.

== triggers type coercion; === never does

0 == "";      // true  — both coerced to a comparable form before comparing
0 == "0";     // true
"" == "0";    // false — but this one ISN'T, because the coercion rules aren't simply transitive
null == undefined; // true — a specific special case in the spec
1 == true;    // true — true coerces to 1
 
0 === "";     // false — different types, no coercion, immediately not equal
"" === "0";   // false
null === undefined; // false

== (loose equality) coerces both operands toward a common type before comparing, following a specific algorithm defined in the language spec — but that algorithm isn't simply "convert everything to the same type and compare," which is exactly why 0 == "" and "" == "0" are true and false respectively, despite looking like they should be consistent. === (strict equality) skips coercion entirely: if the operands are different types, the result is immediately false, no algorithm to reason through.

Why the practical answer is simply "always use ==="

The coercion rules behind == are genuinely learnable — the language spec defines them precisely, not randomly — but memorizing them well enough to predict every real comparison correctly isn't a practical use of effort when === sidesteps the whole problem. This is why === (and !==) is close to a universal recommendation in modern JavaScript style guides and linters: it removes an entire category of subtle bug (a comparison silently returning true or false for the "wrong" reason) at zero real cost, since explicit type conversion (Number(x), String(x), Boolean(x)) is always available when coercion is genuinely intended.

Truthy and falsy: coercion in a boolean context

if ("") { }          // falsy — skipped
if (0) { }            // falsy — skipped
if (null) { }          // falsy — skipped
if (undefined) { }      // falsy — skipped
if (NaN) { }              // falsy — skipped
if ("0") { }               // TRUTHY — a non-empty string, even though it "looks like" zero
if ([]) { }                 // TRUTHY — an empty array is still an object, and all objects are truthy

Exactly six values are falsy in JavaScript: false, 0, "", null, undefined, and NaN — every other value, including "0" (a non-empty string) and [] (an empty array, still an object), is truthy. This list is short enough to genuinely memorize, and doing so resolves a real, common source of confusion — an empty array or a string containing just "0" are both easy to mistakenly assume are falsy, when neither actually is.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What does `typeof null` return, and why?

2. Why is `0 == ""` true but `"" == "0"` false, even though both seem to involve similar-looking empty/zero values?

3. Which of these are the only falsy values in JavaScript?