JavaScript Type Coercion: The Good, the Bad, and the Actually-Useful
Why [] + [] is an empty string, why NaN !== NaN, and the one rule that explains almost every coercion 'gotcha' people love to screenshot.
"JavaScript is weird" is usually a coercion joke — `[] + []` is `''`, `[] + {}` is `'[object Object]'`, `'5' - 1` is `4` but `'5' + 1` is `'51'`. These aren't random. Every one of them follows the exact same, learnable rule.
The rule underneath all of it
`+` prefers strings; every other arithmetic operator (`-`, `*`, `/`) prefers numbers. When either operand of `+` is a string, JavaScript converts the other operand to a string too and concatenates. For every other arithmetic operator, it converts both operands to numbers first.
'5' + 1 // '51' — + sees a string, converts 1 to '1', concatenates
'5' - 1 // 4 — - always converts to number first
[] + [] // '' — arrays convert to '' via toString(), then '' + '' is ''
[] + {} // '[object Object]' — same rule, different toString() resultWhy NaN !== NaN, and why it matters
`NaN` (Not a Number) is the result of a failed numeric conversion — `Number('abc')`, `0/0`. By the IEEE 754 floating-point spec JavaScript's numbers follow, `NaN` is defined to not equal itself, on purpose, so that any computation that touches an invalid number stays visibly invalid instead of silently comparing equal to something. The practical fix is `Number.isNaN(x)`, not `x === NaN` (which is always `false`, including for `NaN` itself).
The genuinely useful part: `==` vs `===`
`==` triggers coercion before comparing; `===` never coerces. `'5' == 5` is `true`; `'5' === 5` is `false`. The practical rule the coercion rabbit hole actually produces: default to `===` everywhere, because it removes an entire category of "wait, why is this true" bugs. Reach for `==` only in the one case it's genuinely idiomatic — `x == null`, which catches both `null` and `undefined` in a single comparison.