After hoisting, interviewers love dropping one-liners like:
console.log([] + []);
console.log([] + {});
console.log({} + []);
console.log(NaN === NaN);
…and watching whether you guess, freeze, or calmly walk the coercion rules.
This post is only output-based type coercion / equality questions.
Try each snippet yourself first. Answers are hidden — click Show answer when you’re ready.
TL;DR — what interviewers are testing
| Concept | Trap |
|---|---|
+ with objects/arrays |
Often becomes string concat, not math |
[] / {} stringification |
[] → "", {} → "[object Object]"
|
Bare {} + []
|
Parser may treat {} as a block, not an object |
NaN === NaN |
Always false — use Number.isNaN / Object.is
|
== vs ===
|
== coerces; === does not |
| Falsy vs “empty-looking” |
[] and {} are truthy
|
typeof null |
Infamous "object" lie |
One-line mental model
+asks both sides to become primitives.
If either side is a string (after that), you get concatenation.
Otherwise you get number math — and weird values becomeNaN.
Warm-up: how + really decides
When JS hits a + b, it roughly does:
1) Convert both sides to primitives (ToPrimitive)
2) If either result is a string → String(a) + String(b) // concat
3) Else → Number(a) + Number(b) // math
For plain objects / arrays, ToPrimitive usually ends up calling .toString():
| Value | String(value) |
Number(value) |
|---|---|---|
[] |
"" |
0 |
[1, 2] |
"1,2" |
NaN |
{} |
"[object Object]" |
NaN |
null |
"null" |
0 |
undefined |
"undefined" |
NaN |
true |
"true" |
1 |
false |
"false" |
0 |
That’s enough to solve most [] + {} style questions.
How to use this post
- Read the snippet
- Say the output out loud (or write it down)
- Only then open Show answer
- Read the step-by-step — don’t only memorize the final print
Q1 — Classic [] + []
console.log([] + []);
(empty string — looks like a blank line) Step by step Interview tip: People often say Show answer
Output
""
+ wants primitives from both arrays.String([]) → "" (empty array joins to empty string)."" + "" → "".0 or []. Wrong. Empty array stringifies to "", so you get string concat of nothing.
Q2 — [] + {}
console.log([] + {});
Step by step Why not math? Because after Show answer
Output
[object Object]
String([]) → ""
String({}) → "[object Object]"
"" + "[object Object]" → "[object Object]"
ToPrimitive, you already have a string on the left (""), so + stays in string mode.
Q3 — ({} + []) with parentheses
console.log({} + []);
console.log(({} + []));
Step by step Same printable result as Show answer
Output
[object Object]
[object Object]
console.log(...) / parentheses, {} is an object literal.String({}) → "[object Object]"
String([]) → ""
"[object Object]" + "" → "[object Object]"
[] + {}, different operand order — both sides still stringify.
Q4 — The famous bare {} + [] trap
eval('{} + []');
eval('({} + [])');
Step by step Interview tip: Always ask: “Is this a statement or an expression?” Parentheses (or Show answer
Output
0
[object Object]
{} + [] at statement start: JS can parse {} as an empty block, not an object.+[].+[] → Number([]) → 0.({} + []) forces an expression → object + array → "[object Object]".console.log) change the parse.
Q5 — Arrays with values still concat
console.log([1, 2] + [3, 4]);
Step by step No spaces. No nested array. Just two strings glued together.Show answer
Output
1,23,4
String([1, 2]) → "1,2"
String([3, 4]) → "3,4"
"1,2" + "3,4" → "1,23,4"
Q6 — NaN === NaN
console.log(NaN === NaN);
console.log(NaN == NaN);
Step by step Correct checks Interview tip: Saying “use Show answer
Output
false
false
=== and == return false.
Number.isNaN(NaN); // true ← prefer this
Object.is(NaN, NaN); // true
isNaN('hello'); // true ← coerces first (sloppy)
Number.isNaN('hello'); // false ← no coercion
isNaN” is incomplete. Prefer Number.isNaN or Object.is.
Q7 — How NaN is born (and stays sticky)
console.log(Number('abc'));
console.log('abc' - 1);
console.log(undefined + 1);
console.log(NaN + 5);
console.log(typeof NaN);
Step by step One-liner: Show answer
Output
NaN
NaN
NaN
NaN
number
NaN.NaN → NaN (it “poisons” the expression).typeof NaN is still "number" — weird but true.NaN means “invalid number,” not “not a number type.”
Q8 — Object.is vs === (NaN + -0)
console.log(Object.is(NaN, NaN));
console.log(Object.is(+0, -0));
console.log(+0 === -0);
Step by step Use this when they ask “when is Show answer
Output
true
false
true
Object.is treats NaN as equal to NaN.Object.is treats +0 and -0 as different.=== does the opposite on zeros: +0 === -0 is true.Object.is not the same as ===?”
Q9 — + vs - with strings
console.log('5' + 2);
console.log('5' - 2);
console.log('5' * '2');
console.log('5' + '2');
Step by step Rule of thumb: Only Show answer
Output
52
3
10
52
+ with a string → concat → "52".- / * always do numeric conversion → 3, 10.'5' + '2' is pure string concat → "52".+ is overloaded for strings. -, *, /, % are always math.
Q10 — Booleans in math
console.log(true + true);
console.log(true + false);
console.log(true + '1');
console.log(false - true);
Step by stepShow answer
Output
2
1
true1
-1
true → 1, false → 0 in numeric context.true + true → 1 + 1 → 2.true + '1' hits a string → "true" + "1" → "true1".false - true → 0 - 1 → -1.
Q11 — Unary plus / Number on [] and {}
console.log(+[]);
console.log(+{});
console.log(Number([]));
console.log(Number({}));
Step by step This is why bare Show answer
Output
0
NaN
0
NaN
+[] → Number([]) → Number("") → 0.+{} → Number("[object Object]") → NaN.{} + [] can print 0 (unary plus on []).
Q12 — null / undefined with +
console.log(null + 1);
console.log(undefined + 1);
console.log([] + null);
console.log(null + []);
Step by step Same characters printed for the last two — both are the string Show answer
Output
1
NaN
null
null
null → 0, so null + 1 → 1.undefined → NaN, so undefined + 1 → NaN.+ becomes string concat: "" + "null" → "null"."null".
Q13 — null == undefined (and friends)
console.log(null == undefined);
console.log(null === undefined);
console.log(null == 0);
console.log(undefined == 0);
console.log(null == false);
Step by step Memorize this trio: Show answer
Output
true
false
false
false
false
null == undefined is true.=== checks type too → false.null / undefined do not loosely equal 0 or false.null == undefined is the exception people forget.
Q14 — Empty array vs empty string vs 0
console.log([] == 0);
console.log([] == '');
console.log('' == 0);
console.log([] == false);
console.log(false == '');
Step by step (high level) Interview tip: This is why seniors say never use Show answer
Output
true
true
true
true
true
== keeps coercing until it can compare primitives.[] becomes "", then often 0.false becomes 0.0 / "".== unless you mean it. Prefer ===.
Q15 — The viral [] == ![]
console.log(![]);
console.log([] == ![]);
Step by step Looks impossible. Totally consistent with coercion rules.Show answer
Output
false
true
[] is truthy, so ![] → false.[] == false.false → 0, [] → "" → 0.0 == 0 → true.
Q16 — Truthy / falsy surprises
console.log(Boolean([]));
console.log(Boolean({}));
console.log(Boolean(''));
console.log(Boolean(0));
console.log(Boolean(NaN));
console.log(Boolean(null));
console.log(!!'0');
Falsy list (memorize — only these): Everything else is truthy — including Show answer
Output
true
true
false
false
false
false
true
false, 0, -0, 0n, '', null, undefined, NaN
[], {}, and '0'.
Q17 — Reference equality for objects/arrays
console.log([] === []);
console.log({} === {});
console.log([] == []);
console.log({} == {});
const a = [];
console.log(a === a);
Step by stepShow answer
Output
false
false
false
false
true
{} / [] literal creates a new value in memory.true.== does not deep-compare objects either.
Q18 — typeof null classic
console.log(typeof null);
console.log(typeof []);
console.log(typeof {});
console.log(typeof NaN);
console.log(Array.isArray([]));
Step by step Safe null check: Show answer
Output
object
object
object
number
true
typeof null === "object" is a decades-old language bug/legacy quirk."object"; detect with Array.isArray.NaN is a number.value === null (not typeof).
Q19 — Floating point equality
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
Why: Binary floating point can’t represent Show answer
Output
0.30000000000000004
false
0.1 / 0.2 exactly.
Not a coercion bug — a precision bug. Mention Number.EPSILON or decimal libraries if they push deeper.
Q20 — Mixed bag rapid fire
console.log([] + {});
console.log({} + []);
console.log([] + [] + 'ok');
console.log('' + 1 + 0);
console.log('' - 1 + 0);
console.log(true + true + true);
console.log(!![]);
console.log(Number.isNaN(NaN) && !(NaN === NaN));
Quick whyShow answer
Output
[object Object]
[object Object]
ok
10
-1
3
true
true
console.log, {} is an expression)."" + "" + "ok" → "ok"."" + 1 + 0 → "10" (left-to-right concat)."" - 1 becomes numeric -1, then -1 + 0 → -1.true thrice → 3.[] truthy → true.=== self-inequality both hold → true.
Practice round (no spoilers)
Predict each line, then check in a REPL.
P1
console.log([] + []);
console.log([] + 1);
console.log([1] + [2]);
Show answer
1
12
"", then "1", then "1"+"2".
P2
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
console.log(Number.isNaN('NaN'));
Show answer
false
true
false
Number.isNaN does not coerce strings.
P3
console.log([] == ![]);
console.log({} == !{});
Show answer
true
false
!{} → false → {} == false → NaN == 0 path fails → false.
P4
console.log(null + null);
console.log(undefined + undefined);
console.log(null == undefined);
Show answer
0
NaN
true
null → 0; undefined → NaN; loose equal special case.
P5
console.log('10' - 5 + '5');
console.log('10' + 5 - '5');
Left: Show answer
55
100
(10-5)+"5" → "55". Right: "105" - "5" → 100.
Cheat sheet — say this in the interview
1. + with a string involved → concatenation
2. [] → "" → 0 | {} → "[object Object]" → NaN
3. Bare {} at statement start may be a block
4. NaN === NaN → false; use Number.isNaN / Object.is
5. Prefer ===; == is a coercion maze
6. Only these are falsy: false, 0, -0, 0n, '', null, undefined, NaN
7. typeof null → "object" (legacy)
Wrap-up
Coercion questions in JS interviews are almost always output prediction, not definitions.
Master these four:
- How
+chooses concat vs math - What
[]/{}become as string/number - Why
NaN === NaNisfalse - Why
==makes[] == ![]“true”
Everything else is a remix of those rules.
If useful, I can do a follow-up in the same format for this binding, closures, or optional chaining / nullish coalescing output traps.
Top comments (0)