Back to JavaScript
JavaScript
medium
mid

How does triple equals differ from double equals, and how does type coercion work 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`.

4 min read·~6 min to think through

=== 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.

js
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:

js
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      // false

What "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:

js
if (x == null) { ... } // true for BOTH null and undefined — and nothing else

This 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.

Senior engineer discussion

Seniors explain coercion as the single distinguishing factor, can walk through a tricky case like `[] == ![]`, recommend === universally, defend the `== null` idiom as the lone exception, and mention Object.is for the NaN/-0 edge cases.

Related questions