Type Coercion in JavaScript
Type coercion is JavaScript's automatic or implicit conversion of values from one type to another. Understanding how and when JavaScript coerces types is crucial for writing bug-free code and is one of the most commonly tested topics in technical interviews.
Implicit vs Explicit Coercion
JavaScript performs type conversion in two ways:
Explicit Coercion
When you intentionally convert a value using built-in functions:
// String conversion
String(123) // "123"
String(true) // "true"
String(null) // "null"
String(undefined) // "undefined"
String([1, 2, 3]) // "1,2,3"
String({ a: 1 }) // "[object Object]"
// Number conversion
Number("123") // 123
Number("123abc") // NaN
Number("") // 0
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number([]) // 0
Number([5]) // 5
Number([1, 2]) // NaN
// Boolean conversion
Boolean(0) // false
Boolean("") // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean("hello") // true
Boolean(42) // true
Boolean([]) // true (!)
Boolean({}) // true (!)
Implicit Coercion
When JavaScript automatically converts types during operations:
// String coercion with +
"5" + 3 // "53" (number → string)
"5" + true // "5true"
"5" + null // "5null"
"5" + undefined // "5undefined"
// Number coercion with arithmetic operators
"5" - 3 // 2 (string → number)
"5" * "2" // 10
"10" / "2" // 5
"5" - true // 4 (true → 1)
"5" - null // 5 (null → 0)
// Boolean coercion in conditions
if ("hello") { } // truthy
if (0) { } // falsy
if ([]) { } // truthy (!)
if ({}) { } // truthy (!)
Truthy and Falsy Values
Every value in JavaScript has an inherent boolean value when evaluated in a boolean context.
Falsy Values (Only 8!)
// These are ALL the falsy values in JavaScript:
false
0
-0
0n // BigInt zero
"" // empty string
null
undefined
NaN
// Everything else is truthy!
Common Truthy Gotchas
// These are all TRUTHY (often surprising!)
Boolean("0") // true - non-empty string
Boolean("false") // true - non-empty string
Boolean([]) // true - empty array is truthy!
Boolean({}) // true - empty object is truthy!
Boolean(function(){}) // true
Boolean(-1) // true - any non-zero number
Boolean(" ") // true - string with space
Boolean(new Boolean(false)) // true - object wrapper!
Equality Operators: == vs ===
This is one of the most frequently asked interview questions about JavaScript.
Strict Equality (===)
Compares both value AND type. No coercion occurs.
5 === 5 // true
5 === "5" // false (different types)
null === undefined // false (different types)
NaN === NaN // false (NaN is never equal to itself!)
[] === [] // false (different object references)
{} === {} // false (different object references)
// For objects, === compares references
const arr1 = [1, 2, 3];
const arr2 = arr1;
arr1 === arr2 // true (same reference)
Abstract Equality (==)
Performs type coercion before comparison. The rules are complex:
// Same type: behaves like ===
5 == 5 // true
"hello" == "hello" // true
// null and undefined are only equal to each other
null == undefined // true
null == 0 // false
undefined == 0 // false
// Number vs String: string → number
5 == "5" // true ("5" → 5)
0 == "" // true ("" → 0)
0 == "0" // true ("0" → 0)
// Boolean vs anything: boolean → number first
true == 1 // true (true → 1)
false == 0 // true (false → 0)
true == "1" // true (true → 1, "1" → 1)
false == "" // true (false → 0, "" → 0)
// Object vs primitive: object → primitive (valueOf/toString)
[1] == 1 // true ([1].toString() → "1" → 1)
["1"] == 1 // true
[1, 2] == "1,2" // true ([1,2].toString() → "1,2")
The Infamous Gotchas
// Empty array comparisons
[] == false // true ([] → "" → 0, false → 0)
[] == ![] // true ([] → 0, ![] → false → 0)
[] == 0 // true
[] == "" // true
// But remember:
Boolean([]) // true (empty array is truthy!)
// This creates confusion:
if ([]) {
console.log("truthy!"); // This runs!
}
if ([] == false) {
console.log("equals false!"); // This also runs!
}
// The transitivity trap
"0" == 0 // true
0 == "" // true
"0" == "" // false (!)
The + Operator's Dual Nature
The + operator is particularly tricky because it handles both addition and string concatenation:
// Addition (both operands are numbers)
5 + 3 // 8
// Concatenation (at least one operand is a string)
"5" + 3 // "53"
5 + "3" // "53"
"5" + "3" // "53"
// Coercion order matters
1 + 2 + "3" // "33" (left-to-right: 1+2=3, 3+"3"="33")
"1" + 2 + 3 // "123" ("1"+2="12", "12"+3="123")
// Unary + converts to number
+"5" // 5
+"" // 0
+true // 1
+false // 0
+null // 0
+undefined // NaN
+[] // 0
+[5] // 5
+{} // NaN
Object to Primitive Coercion
When an object needs to be converted to a primitive, JavaScript uses these methods:
The Coercion Algorithm
- If a
Symbol.toPrimitivemethod exists, call it - Otherwise, for "number" hint: try
valueOf(), thentoString() - For "string" hint: try
toString(), thenvalueOf()
const obj = {
valueOf() {
return 42;
},
toString() {
return "hello";
}
};
// Number context uses valueOf
Number(obj) // 42
obj + 0 // 42
// String context uses toString
String(obj) // "hello"
obj + "" // "42" (+ prefers valueOf for objects!)
// Using Symbol.toPrimitive (takes precedence)
const custom = {
[Symbol.toPrimitive](hint) {
if (hint === "number") return 10;
if (hint === "string") return "ten";
return true; // default
}
};
Number(custom) // 10
String(custom) // "ten"
custom + "" // "true"
Practical Patterns and Best Practices
Safe Type Checking
// Checking for null or undefined
if (value == null) {
// This catches BOTH null and undefined
// This is one of the few valid uses of ==
}
// Better: explicit checks
if (value === null || value === undefined) { }
// Or use nullish coalescing
const result = value ?? "default";
// Type checking
typeof value === "string"
typeof value === "number"
typeof value === "boolean"
Array.isArray(value)
value instanceof Date
// Checking for NaN (special case!)
Number.isNaN(value) // correct
value !== value // also works (NaN !== NaN)
isNaN(value) // careful - coerces first!
isNaN("hello") // true (coerced to NaN)
Number.isNaN("hello") // false (no coercion)
Converting to Boolean Safely
// Double negation (common pattern)
!!value // converts to boolean
// Explicit conversion
Boolean(value)
// Watch out for these:
!!"0" // true (non-empty string)
!!"false" // true (non-empty string)
!![] // true (empty array)
!!{} // true (empty object)
Converting to Number Safely
// parseInt and parseFloat
parseInt("42") // 42
parseInt("42px") // 42 (stops at non-digit)
parseInt("") // NaN
parseInt("0x10") // 16 (hexadecimal)
parseInt("10", 2) // 2 (binary)
parseFloat("3.14") // 3.14
parseFloat("3.14abc") // 3.14
// Number() is stricter
Number("42") // 42
Number("42px") // NaN (requires entire string)
Number("") // 0 (!)
// Unary plus
+"42" // 42
+"42px" // NaN
Interview Tips
- Always use
===unless you have a specific reason for== - Know the 8 falsy values - everything else is truthy
- Remember
[]and{}are truthy but[] == falseis true - The
+operator concatenates if either operand is a string null == undefinedis true, butnull === undefinedis falseNaNis never equal to anything, including itself- Use
Number.isNaN()instead of globalisNaN() - Prefer explicit coercion for readability:
String(x),Number(x),Boolean(x)
Common Interview Questions
Test your understanding with these classic questions:
// What does each expression evaluate to?
// 1. Comparison operators
[] == ![] // ?
[] == false // ?
"0" == false // ?
// 2. Arithmetic
"5" - 3 // ?
"5" + 3 // ?
"5" - - "3" // ?
// 3. Boolean context
if ([]) { } // runs or not?
if ([] == false) { } // runs or not?
// 4. NaN behavior
NaN === NaN // ?
Number.isNaN(NaN) // ?
Number.isNaN("NaN") // ?
Understanding type coercion deeply will help you avoid subtle bugs and confidently answer interview questions about JavaScript's type system.
Practice Problems
Truthy and Falsy Values
Equality Operators
Type Coercion with Operators
NaN Comparisons
Empty Array Coercion
Let's continue exploring the next page. Take your time, and proceed when you're ready.