How does === differ from ==? Explain type coercion with examples.
=== is strict equality: same type AND same value, no conversion. == is loose equality: it coerces operands to a common type first, with surprising rules (null == undefined, '' == 0, [] == ![]). Rule: always use === except the one idiom `x == null`.
=== and == differ in one thing: whether they coerce types before comparing.
=== — strict equality
No type conversion. If the types differ, the result is false immediately. Otherwise it compares values.
1 === 1 // true
1 === "1" // false — number vs string, no coercion
null === undefined // false — different types
NaN === NaN // false — NaN is never equal to anything== — loose equality (with coercion)
If the types differ, JS coerces the operands toward a common type, then compares. The rules are notoriously surprising:
1 == "1" // true — string coerced to number
0 == false // true — boolean → number 0
"" == 0 // true — "" → 0
"" == false // true
null == undefined // true — special-cased
null == 0 // false — null only loosely equals undefined (and itself)
[] == false // true — [] → "" → 0, false → 0
[] == ![] // true — the infamous one: ![] is false → 0, [] → 0
NaN == NaN // falseWhat "type coercion" means
Coercion is JS automatically converting a value from one type to another. It happens with ==, with + (1 + "2" → "12"), in if conditions (truthy/falsy), etc.
== roughly coerces like this: null/undefined are special-cased to equal only each other; otherwise strings/booleans get converted to numbers, and objects get converted to primitives (valueOf/toString) before comparing. The chain of conversions is what produces the weird results.
The practical rule
Always use ===. It's predictable — no hidden conversions, easier to reason about, and linters enforce it.
The one accepted exception is the idiom:
if (x == null) { ... } // true for BOTH null and undefined — and nothing elseThis is a deliberate, well-known shorthand for "null or undefined."
The framing
"=== compares type and value with no conversion; == coerces the operands to a common type first, and those coercion rules produce traps like '' == 0 and [] == ![] both being true. Type coercion is JS auto-converting types — it's also why 1 + '2' is '12'. The rule is simple: use === everywhere; the only blessed == use is x == null to catch null and undefined together."
Follow-up questions
- •Why is [] == ![] true?
- •Why is `x == null` an accepted use of loose equality?
- •What's the difference between == and Object.is?
- •How does coercion work in `1 + '2'` vs `1 - '2'`?
Common mistakes
- •Using == out of habit and hitting coercion bugs.
- •Thinking === does some conversion — it does none.
- •Expecting NaN === NaN to be true.
- •Not knowing the `x == null` idiom and writing `x === null || x === undefined`.
Performance considerations
- •Not a performance concern in practice. === can be marginally faster since it skips coercion, but the real reason to prefer it is correctness and readability, not speed.
Edge cases
- •NaN is not equal to itself with == or === (use Number.isNaN).
- •+0 === -0 is true, but Object.is(+0, -0) is false.
- •Object comparison is by reference, not value, for both operators.
- •Objects coerce via valueOf/toString in == comparisons.
Real-world examples
- •Linters (ESLint eqeqeq) flagging == across most production codebases.
- •The `value == null` guard used widely to handle null and undefined together.