After hoisting, interviewers love dropping one-liners like:
console.log([] + []);
console.log([] + {});
console.log({} + []);
console.log(NaN === NaN);
Enter fullscreen mode Exit fullscreen mode
…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 become
NaN.
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
Enter fullscreen mode Exit fullscreen mode
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([] + []);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
""
Enter fullscreen mode Exit fullscreen mode
(empty string — looks like a blank line)
Step by step
-
+wants primitives from both arrays. -
String([])→""(empty array joins to empty string). -
"" + ""→"".
Interview tip: People often say 0 or []. Wrong. Empty array stringifies to "", so you get string concat of nothing.
Q2 — [] + {}
console.log([] + {});
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
[object Object]
Enter fullscreen mode Exit fullscreen mode
Step by step
- Left:
String([])→"" - Right:
String({})→"[object Object]" - One side is a string → concat →
"" + "[object Object]"→"[object Object]"
Why not math? Because after ToPrimitive, you already have a string on the left (""), so + stays in string mode.
Q3 — ({} + []) with parentheses
console.log({} + []);
console.log(({} + []));
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
[object Object]
[object Object]
Enter fullscreen mode Exit fullscreen mode
Step by step
- Inside
console.log(...)/ parentheses,{}is an object literal. -
String({})→"[object Object]" -
String([])→"" - Concat →
"[object Object]" + ""→"[object Object]"
Same printable result as [] + {}, different operand order — both sides still stringify.
Q4 — The famous bare {} + [] trap
eval('{} + []');
eval('({} + [])');
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
0
[object Object]
Enter fullscreen mode Exit fullscreen mode
Step by step
- Bare
{} + []at statement start: JS can parse{}as an empty block, not an object. - What’s left is unary
+[]. -
+[]→Number([])→0. - Wrap it:
({} + [])forces an expression → object + array →"[object Object]".
Interview tip: Always ask: “Is this a statement or an expression?” Parentheses (or console.log) change the parse.
Q5 — Arrays with values still concat
console.log([1, 2] + [3, 4]);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
1,23,4
Enter fullscreen mode Exit fullscreen mode
Step by step
-
String([1, 2])→"1,2" -
String([3, 4])→"3,4" -
"1,2" + "3,4"→"1,23,4"
No spaces. No nested array. Just two strings glued together.
Q6 — NaN === NaN
console.log(NaN === NaN);
console.log(NaN == NaN);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
false
false
Enter fullscreen mode Exit fullscreen mode
Step by step
- IEEE-754 rule: NaN is never equal to anything, including itself.
- So both
===and==returnfalse.
Correct checks
Number.isNaN(NaN); // true ← prefer this
Object.is(NaN, NaN); // true
isNaN('hello'); // true ← coerces first (sloppy)
Number.isNaN('hello'); // false ← no coercion
Enter fullscreen mode Exit fullscreen mode
Interview tip: Saying “use 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);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
NaN
NaN
NaN
NaN
number
Enter fullscreen mode Exit fullscreen mode
Step by step
- Failed number conversion →
NaN. - Any math with
NaN→NaN(it “poisons” the expression). -
typeof NaNis still"number"— weird but true.
One-liner: 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);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
true
false
true
Enter fullscreen mode Exit fullscreen mode
Step by step
-
Object.istreatsNaNas equal toNaN. -
Object.istreats+0and-0as different. -
===does the opposite on zeros:+0 === -0istrue.
Use this when they ask “when is 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');
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
52
3
10
52
Enter fullscreen mode Exit fullscreen mode
Step by step
-
+with a string → concat →"52". -
-/*always do numeric conversion →3,10. -
'5' + '2'is pure string concat →"52".
Rule of thumb: Only + 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);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
2
1
true1
-1
Enter fullscreen mode Exit fullscreen mode
Step by step
-
true→1,false→0in 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({}));
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
0
NaN
0
NaN
Enter fullscreen mode Exit fullscreen mode
Step by step
-
+[]→Number([])→Number("")→0. -
+{}→Number("[object Object]")→NaN.
This is why bare {} + [] can print 0 (unary plus on []).
Q12 — null / undefined with +
console.log(null + 1);
console.log(undefined + 1);
console.log([] + null);
console.log(null + []);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
1
NaN
null
null
Enter fullscreen mode Exit fullscreen mode
Step by step
- Numeric:
null→0, sonull + 1→1. - Numeric:
undefined→NaN, soundefined + 1→NaN. - With arrays,
+becomes string concat:"" + "null"→"null".
Same characters printed for the last two — both are the string "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);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
true
false
false
false
false
Enter fullscreen mode Exit fullscreen mode
Step by step
- Spec special case:
null == undefinedistrue. -
===checks type too →false. -
null/undefineddo not loosely equal0orfalse.
Memorize this trio: 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 == '');
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
true
true
true
true
true
Enter fullscreen mode Exit fullscreen mode
Step by step (high level)
-
==keeps coercing until it can compare primitives. -
[]becomes"", then often0. -
falsebecomes0. - So these “different-looking” values all meet in the middle as
0/"".
Interview tip: This is why seniors say never use == unless you mean it. Prefer ===.
Q15 — The viral [] == ![]
console.log(![]);
console.log([] == ![]);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
false
true
Enter fullscreen mode Exit fullscreen mode
Step by step
-
[]is truthy, so![]→false. - Expression becomes
[] == false. -
false→0,[]→""→0. -
0 == 0→true.
Looks impossible. Totally consistent with coercion rules.
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');
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
true
true
false
false
false
false
true
Enter fullscreen mode Exit fullscreen mode
Falsy list (memorize — only these):
false, 0, -0, 0n, '', null, undefined, NaN
Enter fullscreen mode Exit fullscreen mode
Everything else is truthy — including [], {}, and '0'.
Q17 — Reference equality for objects/arrays
console.log([] === []);
console.log({} === {});
console.log([] == []);
console.log({} == {});
const a = [];
console.log(a === a);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
false
false
false
false
true
Enter fullscreen mode Exit fullscreen mode
Step by step
- Objects/arrays compare by reference, not contents.
- Each
{}/[]literal creates a new value in memory. - Same variable refers to the same reference →
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([]));
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
object
object
object
number
true
Enter fullscreen mode Exit fullscreen mode
Step by step
-
typeof null === "object"is a decades-old language bug/legacy quirk. - Arrays are objects →
"object"; detect withArray.isArray. -
NaNis a number.
Safe null check: value === null (not typeof).
Q19 — Floating point equality
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
0.30000000000000004
false
Enter fullscreen mode Exit fullscreen mode
Why: Binary floating point can’t represent 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));
Enter fullscreen mode Exit fullscreen mode
Show answerOutput
[object Object]
[object Object]
ok
10
-1
3
true
true
Enter fullscreen mode Exit fullscreen mode
Quick why
- First two: stringified object (inside
console.log,{}is an expression). -
"" + "" + "ok"→"ok". -
"" + 1 + 0→"10"(left-to-right concat). -
"" - 1becomes numeric-1, then-1 + 0→-1. -
truethrice →3. -
[]truthy →true. - Best NaN check +
===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]);
Enter fullscreen mode Exit fullscreen mode
Show answer
1
12
Enter fullscreen mode Exit fullscreen mode
"", then "1", then "1"+"2".
P2
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
console.log(Number.isNaN('NaN'));
Enter fullscreen mode Exit fullscreen mode
Show answerfalse
true
false
Enter fullscreen mode Exit fullscreen mode
Number.isNaN does not coerce strings.
P3
console.log([] == ![]);
console.log({} == !{});
Enter fullscreen mode Exit fullscreen mode
Show answertrue
false
Enter fullscreen mode Exit fullscreen mode
!{} → false → {} == false → NaN == 0 path fails → false.
P4
console.log(null + null);
console.log(undefined + undefined);
console.log(null == undefined);
Enter fullscreen mode Exit fullscreen mode
Show answer0
NaN
true
Enter fullscreen mode Exit fullscreen mode
null → 0; undefined → NaN; loose equal special case.
P5
console.log('10' - 5 + '5');
console.log('10' + 5 - '5');
Enter fullscreen mode Exit fullscreen mode
Show answer55
100
Enter fullscreen mode Exit fullscreen mode
Left: (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)
Enter fullscreen mode Exit fullscreen mode
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.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.