JavaScript Interview Prep
Type System & Coercion

Type Coercion

The Overly Helpful Translator

LinkedIn Hook

"5" + 3 returns "53". "5" - 3 returns 2. [] + [] returns "". {} + [] returns 0.

Most devs call this "weird JavaScript." It isn't. It's an algorithm — written down in the ECMAScript spec — and once you know ToPrimitive, ToNumber, and ToString, every single one of those results becomes predictable.

Type coercion is where JavaScript plays translator. Sometimes it's helpful. Sometimes it turns a bug into a silent wrong answer.

In this lesson you'll learn the difference between explicit and implicit coercion, the three abstract operations the spec uses, why + behaves differently from -, and how to decode expressions like {} + [] step by step.

If an interviewer asks "walk me through why [] + {} is a string but {} + [] is 0," after this lesson you'll answer without hesitation.

Read the full lesson -> [link]

#JavaScript #TypeCoercion #InterviewPrep #ECMAScript #Frontend #CodingInterview #WebDevelopment


Type Coercion thumbnail


What You'll Learn

  • The difference between explicit and implicit coercion — and when each kicks in
  • The three spec-level abstract operations: ToPrimitive, ToNumber, ToBoolean
  • How to decode tricky expressions like [] + [], [] + {}, and {} + [] step by step

JavaScript, The Overly Helpful Translator

Think of JavaScript as an overly helpful translator. When you speak French to someone who only understands English, JS doesn't throw an error — it silently translates your French into English, sometimes getting the meaning right, sometimes hilariously wrong.

Explicit Coercion — You're in Control

When you intentionally convert a value, it's explicit coercion:

// To Number
Number("42");       // 42
Number("hello");    // NaN
Number(true);       // 1
Number(false);      // 0
Number(null);       // 0
Number(undefined);  // NaN
Number("");         // 0
Number(" ");        // 0 -- whitespace-only strings become 0!

// To String
String(42);         // "42"
String(true);       // "true"
String(null);       // "null"
String(undefined);  // "undefined"
String([1, 2, 3]);  // "1,2,3"
String({});         // "[object Object]"

// To Boolean
Boolean(0);         // false
Boolean("");        // false
Boolean(null);      // false
Boolean(undefined); // false
Boolean(NaN);       // false
Boolean(1);         // true
Boolean("hello");   // true
Boolean([]);        // true -- empty array is truthy!
Boolean({});        // true -- empty object is truthy!

Implicit Coercion — JS Does It Behind the Scenes

JavaScript automatically coerces types in certain operations:

// The + operator: if ONE side is a string, it concatenates
"5" + 3;          // "53" -- number 3 becomes "3"
"5" + true;       // "5true"
"5" + null;       // "5null"
"5" + undefined;  // "5undefined"

// The -, *, / operators: ALWAYS convert to number
"5" - 3;          // 2
"5" * "3";        // 15
"6" / "2";        // 3
true + true;      // 2 (1 + 1)
true + false;     // 1 (1 + 0)

// Unary + converts to number
+"42";             // 42
+true;             // 1
+false;            // 0
+"";               // 0
+null;             // 0
+undefined;        // NaN

The Abstract Operations (ECMAScript Spec)

Behind every coercion is one of these internal algorithms:

ToPrimitive: Converts objects to primitives. Calls valueOf() first (for numbers), then toString(). For strings, it calls toString() first, then valueOf().

// How ToPrimitive works on objects
const obj = {
  valueOf() { return 42; },
  toString() { return "hello"; }
};

// Numeric context -- valueOf() is called first
obj + 0;    // 42 (valueOf returned 42)
obj * 2;    // 84

// String context -- toString() is called first
`${obj}`;   // "hello"
String(obj); // "hello"

ToNumber: The rules:

// ToNumber conversion table:
// undefined   -> NaN
// null        -> 0
// true        -> 1
// false       -> 0
// ""          -> 0
// "  "        -> 0 (whitespace trimmed, empty = 0)
// "123"       -> 123
// "12.5"      -> 12.5
// "hello"     -> NaN
// []          -> 0 (ToPrimitive -> "" -> ToNumber -> 0)
// [1]         -> 1 (ToPrimitive -> "1" -> ToNumber -> 1)
// [1,2]       -> NaN (ToPrimitive -> "1,2" -> ToNumber -> NaN)

ToBoolean: Simply checks if the value is in the falsy list (covered in Lesson 8.4).

Tricky Coercion Examples

// Why does [] + [] equal ""?
// Step 1: ToPrimitive([]) -> [].toString() -> ""
// Step 2: "" + "" -> ""
[] + [];  // ""

// Why does [] + {} equal "[object Object]"?
// Step 1: ToPrimitive([]) -> ""
// Step 2: ToPrimitive({}) -> "[object Object]"
// Step 3: "" + "[object Object]" -> "[object Object]"
[] + {};  // "[object Object]"

// Why does {} + [] equal 0?
// The {} at the START of a statement is parsed as an empty block, not an object!
// So it becomes: + [] -> + "" -> 0
{} + [];  // 0 (in console; in expression context it's "[object Object]")

// Grouping forces it to be an expression:
({} + []); // "[object Object]"

Type Coercion visual 1


Common Mistakes

  • Thinking + always means addition — if either operand is a string (or becomes one via ToPrimitive), + concatenates instead of adding.
  • Using parseInt(str) without a radix — always pass 10 for base-10 numbers. Some legacy engines interpret a leading 0 as octal.
  • Forgetting that Number("") and Number(" ") both return 0, not NaN. If you're validating numeric input, check the string first.

Interview Questions

Q: What is the difference between implicit and explicit coercion?

Explicit coercion is when the developer intentionally converts a type using functions like Number(), String(), or Boolean(). Implicit coercion happens automatically when JavaScript encounters operators or contexts that expect a different type -- like "5" + 3 converting 3 to "3".

Q: Why does "5" + 3 produce "53" but "5" - 3 produces 2?

The + operator is overloaded -- when either operand is a string, it does string concatenation. The - operator only works with numbers, so it converts both operands to numbers first.

Q: Explain {} + [] vs [] + {}.

{} + [] in a statement context: the {} is parsed as an empty block, leaving + [] which is +"" which is 0. [] + {} always evaluates as an expression: ToPrimitive([]) is "", ToPrimitive({}) is "[object Object]", so the result is "[object Object]".

Q: What does ToPrimitive do?

ToPrimitive is the spec-level algorithm that converts an object to a primitive based on a hint ("number", "string", or "default"). Under the number hint it calls valueOf() first, then falls back to toString(). Under the string hint it tries toString() first, then valueOf().

Q: What is the result of [] + []? Why?

"". ToPrimitive([]) returns "" (the result of [].toString()), so the expression becomes "" + "" which is "".

Q: What is type coercion?

Type coercion is the automatic or manual conversion of a value from one type to another — for example converting a string to a number, or a boolean to a string. JavaScript performs coercion implicitly in many operators, and explicitly via Number(), String(), Boolean(), and similar conversion functions.


Quick Reference — Cheat Sheet

TYPE COERCION -- QUICK MAP

Explicit (you call it):
  Number(x)   String(x)   Boolean(x)
  +x (unary)  `${x}`      !!x

Implicit (operator triggers it):
  +  with string  -> concatenation   ("5" + 3 = "53")
  -  * /  %       -> ToNumber both   ("5" - 3 = 2)
  if/while/?:     -> ToBoolean
  ==              -> see Lesson 8.3
  ${x}            -> ToString

Abstract operations:
  ToPrimitive(obj, hint)
    hint "number" -> valueOf then toString
    hint "string" -> toString then valueOf
    hint "default"-> usually valueOf then toString
  ToNumber:    "" -> 0, " " -> 0, null -> 0, undefined -> NaN
  ToBoolean:   only 8 falsy values, everything else truthy

Previous: Primitive vs Reference -> Sticky Notes vs Business Cards Next: Equality Operators -> The Strict vs Lenient Librarian


This is Lesson 8.2 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page