Types & coercion
JavaScript data types and the two typeof traps
JavaScript has 7 primitive types (string, number, bigint, boolean, symbol, null, undefined) plus object. Primitives are immutable and compared by value; objects are compared by reference, so {} !== {}.
Two typeof results are famous traps: typeof null === "object" (a bug standardized forever) and typeof fn === "function" (functions are objects, but typeof special-cases them). The difference between null and undefined is intent. undefined means "was never assigned"; null means "empty on purpose".
The three type conversions
Everything coercion-related reduces to three functions the language applies for you:
String(x)is predictable:String(null) → "null",String(123) → "123".Number(x)has the cases to memorize:"" → 0," 12 " → 12,"12x" → NaN,null → 0,undefined → NaN,true → 1.Boolean(x)comes down to the complete falsy list:false, 0, -0, 0n, "", null, undefined, NaN. Everything else is truthy, including"0"," ",[], and{}.
Operators convert on their own rules
1 + "2"; // "12" — + prefers strings: one string side → concat
"6" * "2"; // 12 — every OTHER arithmetic operator goes numeric
+"7"; // 7 — unary + is a terse Number()
+ is the special one. -, *, /, % always convert to numbers.
== vs ===, and NaN
=== compares without any conversion; different types are never equal. == converts first, by an algorithm with surprises (0 == "" is true). The practical junior rule: use === everywhere.
NaN is a number (typeof NaN === "number") that means "a numeric computation failed", and it equals nothing, including itself:
NaN === NaN; // false
Number.isNaN(NaN); // true — the reliable check
The == algorithm behind surprises like [] == 0 being true while null == 0 stays false is covered in the middle notes.
How the == algorithm works
Loose equality applies four rules in order:
- Same types. Behaves like
===. nullandundefined. Equal to each other, and to nothing else. They never convert to numbers here.- Boolean or string vs number. Convert to numbers and retry.
- Object vs primitive. Convert the object to a primitive (below) and retry.
Rule 2 is the one everyone gets wrong: null == 0 is false because no numeric conversion happens, while "" == 0 is true via rule 3 (Number("") is 0).
How objects convert to primitives (valueOf, then toString)
When an object must become a primitive, the engine tries valueOf() first, then toString(); the order flips in explicitly string contexts. Arrays have no useful valueOf, so they stringify. That single fact decodes the party tricks:
[] + []; // "" — "" + ""
[] + {}; // "[object Object]"
[1, 2] == "1,2"; // true — [1,2] → "1,2"
[] == 0; // true — [] → "" → 0
[] == false; // true — both sides → 0... yet:
!![]; // true — [] is an OBJECT, so it's truthy
Watch for the classic split-brain conclusion: "[] == false, so [] is falsy". Being loosely equal to false and being falsy are different questions answered by different algorithms (== vs ToBoolean). An array is an object, so it is always truthy.
Why null >= 0 is true but null == 0 is false
Relational operators (< > <= >=) always convert to numbers, or compare two strings lexicographically. There is no null/undefined exception on this path, which produces a paradox interviewers use to test whether you know rule 2:
null >= 0; // true — relational: Number(null) is 0
null > 0; // false
null == 0; // false — equality: null only equals undefined
Traps with numbers and parsing
0.1 + 0.2 !== 0.3is binary floating point at work; compare with an epsilon, and remembertoFixedreturns a string.Number("12px")givesNaN, butparseInt("12px")gives12. One is a strict whole-string parse, the other a prefix parse. They are not interchangeable.new Number(3) == 3is true,=== 3is false. Nevernewa primitive wrapper.
What passes the hint to the object-to-primitive conversion, Symbol.toPrimitive, and the cases where coercion is idiomatic all live in the senior notes.
How ToPrimitive hints work
The middle-level "valueOf then toString" is the default behavior of a spec algorithm called ToPrimitive, driven by a hint: "number" (arithmetic, relational <), "string" (template literals, property keys), or "default" (+ and ==, which can't know which you meant). An object can take over the whole thing with Symbol.toPrimitive:
const price = {
amount: 99,
[Symbol.toPrimitive](hint) {
return hint === "string" ? "$99" : this.amount;
},
};
`${price}`; // "$99" — hint "string"
price + 1; // 100 — hint "default"
price < 100; // true — hint "number"
Interviewers grade this by asking you to walk [] + {} through the hints and the valueOf/toString fallback instead of reciting the memorized output.
What IEEE-754 doubles mean for JavaScript numbers
All JS numbers are 64-bit doubles with a 53-bit mantissa. Integers are exact only up to Number.MAX_SAFE_INTEGER (2⁵³−1); beyond it, x + 1 === x + 2 can be true. 0.1 + 0.2 is deterministic rounding, not a bug. Use Number.EPSILON for comparisons, and integer cents or BigInt for money. There are two zeros: 0 === -0, but Object.is(0, -0) is false and 1 / -0 is -Infinity. BigInt gives exact integers yet refuses to mix with numbers in arithmetic (TypeError), and 1n == 1 is true while 1n === 1 is false.
The three-and-a-half equality algorithms
==. Coercing (the four rules).===. Strict, exceptNaN !== NaNand+0 === -0.Object.is. Like===with two fixes:Object.is(NaN, NaN)is true,Object.is(0, -0)is false.- SameValueZero. The half:
NaNequalsNaN, but+0equals-0. This is whatArray.prototype.includes,Mapkeys andSetuse, which is why[NaN].includes(NaN)is true while[NaN].indexOf(NaN)is −1.
When coercion is fine
"Never use ==" is a junior rule stated with senior confidence. The mature version: x == null is the idiomatic check for "null or undefined", and it is safe precisely because of rule 2. Truthiness guards are fine for values that can't legitimately be 0 or "". Template literals coerce by design. Meanwhile document.all being loosely equal to null is a willful spec violation kept for web compatibility, and typeof undeclared not throwing is the one safe way to probe a global.
beta
The interviewer part is in the works.
The diagnostic, personal maps, and AI mock interviews are being finished right now. The notes stay free either way. Leave an email and you'll get the first-cohort invite, plus a month of Pro when it opens.