JavaScript has two equality operators: == (loose equality) and === (strict equality). They look similar, but their behavior differs dramatically. This post is about why you should almost always reach for === — and what happens when you don't.

TL;DR

Always use === and !==. The mental overhead of remembering type coercion rules isn't worth it. Style guides from Airbnb, Google, and MDN all agree.

1. The Core Difference: Type Coercion

The key distinction is this: == performs type coercion — JavaScript converts operands to the same type before comparing. === compares both value and type without any conversion.

This single difference causes most surprises. Let's see what happens in practice:

javascript — type coercion gotchas
console.log(0 == "0");          // true  ← string coerced to number
console.log(0 == false);        // true  ← false → 0
console.log("" == false);       // true  ← both become 0
console.log(null == undefined); // true  ← special rule
console.log(" \t\n" == 0);      // true  ← whitespace coerced to 0

// With ===, all of these are false. As expected.
console.log(0 === "0");         // false ✓
console.log(0 === false);       // false ✓
console.log(null === undefined);// false ✓

Every one of those == comparisons is technically "true." But are any of them actually equal in a way that makes sense for your code? Almost certainly not.

2. Real Bugs from the Wild

Here's the thing about type coercion bugs — they're not abstract. They show up in real applications, cause real production issues, and take real hours to debug. Here are three patterns I've seen trip people up:

Bug 1: Form input as a string

Form inputs always return strings. Always. Even type="number" gives you a string until you parse it. This trips up so many beginners:

javascript — the form input trap
// User typed "0" into a text input
let userAge = "0";           // It's a string!

if (userAge == 0) {
  console.log("User is newborn or empty input"); // This runs! Bug.
}

// The === version makes the type explicit
if (userAge === 0) {
  // This never runs. Which is correct — userAge is a string.
}

// Proper approach: parse first, then compare
let parsedAge = Number(userAge);
if (parsedAge === 0) {
  console.log("Age is zero"); // Now intentional
}

Bug 2: The NaN trap

NaN is one of JavaScript's weird corners. It doesn't equal itself — not even with ===. And with ==, the confusion doubles:

javascript — NaN is never equal to NaN
let result = parseInt("abc");   // Returns NaN

if (result == NaN) {            // false — NaN != NaN!
  // This never runs — silent bug
}

if (result === NaN) {           // Also false! NaN !== NaN
  // Still never runs
}

// Correct ways to check for NaN:
if (isNaN(result)) { ... }      // true ✓ (but also coerces non-numbers)
if (Number.isNaN(result)) { ... } // true ✓ (strict, no coercion)
Key insight

Use Number.isNaN() over isNaN(). The global isNaN() coerces its argument first, so isNaN("hello") returns true. Number.isNaN("hello") correctly returns false.

Bug 3: Falsy value surprises

JavaScript has six falsy values: false, 0, "", null, undefined, and NaN. With ==, many of these are considered equal to each other — which is almost never what you want:

javascript — falsy comparisons
let quantity = "";    // Empty form field

if (quantity == 0) {
  console.log("Out of stock"); // Runs! Because "" == 0
}

// With ===:
if (quantity === 0) {
  // Doesn't run. "" is not 0. Correct behavior.
}

// Even better — check for what you actually mean:
if (quantity === "" || quantity === undefined) {
  console.log("No quantity provided");
}
if (Number(quantity) === 0) {
  console.log("Quantity is zero");
}

3. The Mental Model Problem

Even if you know all the coercion rules — and there are many — using == forces anyone reading your code (including future you) to mentally simulate type coercion to understand what's happening. That's cognitive overhead that buys you nothing.

=== is explicit. It says: "I want these to be exactly equal, same type, same value." There's no ambiguity, no hidden rules, no surprises.

4. What the Experts Say

This isn't just my opinion. Here's what major style guides recommend:

From the Airbnb JavaScript Style Guide
// ✗ bad
if (a == b) { ... }

// ✓ good  
if (a === b) { ... }

MDN documentation consistently recommends strict equality. ESLint's eqeqeq rule (which most teams enable) enforces this automatically. Kyle Simpson in "You Don't Know JS" explains the coercion rules exhaustively — and even he recommends === when in doubt.

5. The Legitimate Uses of ==

There's one common pattern where == is actually useful — checking for null or undefined simultaneously:

javascript — the one good use of ==
// This checks for both null AND undefined
if (value == null) {
  console.log("value is null or undefined");
}

// Equivalent to:
if (value === null || value === undefined) { ... }

// The == version is shorter, and the intent is clear to experienced devs.
// But I'd still recommend the explicit === version in most codebases
// for consistency and readability.

Summary

The rule is simple: always use === and !==. The one exception (== null) is narrow and optional — and worth the tradeoff of strict consistency.

Type coercion with == isn't just confusing — it actively hides bugs. Bugs that are hard to spot, hard to trace, and completely avoidable. The extra character is worth it every time.

Personal note

I spent 4 hours once debugging a comparison issue in a form handler. It was ==. That was the day I added ESLint's eqeqeq rule to every project I start.

A
Akash Das Dhibar
Web developer from West Bengal, India. Learning in public and writing about JS fundamentals.
GitHub LinkedIn
← Previous All posts →