How JavaScript converts between types implicitly and explicitly.
JavaScript is a dynamically typed language. Variables have no declared type, and the engine freely converts values between types at runtime — sometimes automatically, sometimes when you explicitly ask it to. Understanding when and how these conversions happen is essential for writing predictable code and avoiding the class of bugs that come from unexpected type coercion.
Explicit conversion (also called type casting) is when you deliberately convert a value using a built-in function or constructor: Number("42"), String(99), Boolean(0).Implicit conversion (also called type coercion) is when the engine converts a value automatically as part of evaluating an expression — for example, when you use == or apply the + operator to mixed types.
The + operator has two jobs: numeric addition and string concatenation. When either operand is a string, it concatenates. When neither is a string, it adds numerically. This leads to some non-obvious results:
1 + 2 // 3 — both numbers"1" + 2 // "12" — string + number → concatenation1 + "2" // "12" — number + string → concatenation1 + 2 + "3" // "33" — (1+2)=3, then 3+"3"="33""1" + 2 + 3 // "123" — "1"+"2"="12", then "12"+"3"="123"true + 1 // 2 — true coerced to 1false + 1 // 1 — false coerced to 0null + 1 // 1 — null coerced to 0undefined + 1 // NaN — undefined coerced to NaN[] + [] // "" — both become ""[] + {} // "[object Object]"{} + [] // 0 — {} parsed as empty block, then +[] = 0
The last two examples show how the + operator can produce wildly unintuitive results with non-primitive values. Avoid mixing arrays and objects with + in production code.
This is one of the most discussed topics in JavaScript. The strict equality operator === compares both value and type — no conversion occurs. The loose equality operator == applies a set of coercion rules before comparing.
// Strict equality — no coercion1 === 1 // true1 === "1" // false — different typesnull === undefined // false// Loose equality — with coercion1 == "1" // true — "1" converted to 10 == false // true — false converted to 00 == "" // true — "" converted to 0null == undefined // true — special rulenull == 0 // false — null only == undefinedNaN == NaN // false — NaN is never equal to anything
The loose equality algorithm is complex and has many special cases. The most important rules are:
1
If both operands have the same type
Compare directly (like ===), with the exception that NaN !== NaN.
2
null and undefined are loosely equal to each other only
null == undefined is true, but null == 0 and undefined == 0 are both false.
3
If one operand is a number and the other is a string
The string is converted to a number before comparison.
4
If one operand is a boolean
The boolean is converted to a number (true → 1, false → 0) first.
5
If one operand is an object and the other is a primitive
The object’s valueOf() or toString() is called to convert it to a primitive.
Prefer === in all comparisons unless you have a specific reason to use ==. The most common legitimate use of == is checking for null or undefined simultaneously: if (value == null) catches both null and undefined in one check.
When an object is used in a context that expects a primitive (like in a numeric operation or string concatenation), JavaScript calls one of two methods on it:
valueOf() — called first in numeric contexts. If it returns a primitive, that value is used.
toString() — called if valueOf() does not return a primitive, or in string contexts.
const obj = { valueOf() { return 42; }, toString() { return "forty-two"; }};obj + 1 // 43 — valueOf called`${obj}` // "42" — toString called in template (or valueOf if it returns a string)String(obj) // "42" — toString, but valueOf returned a number so it converts that
Arrays convert to strings by joining their elements with commas: [1, 2, 3].toString() → "1,2,3". An empty array becomes an empty string, which is why Number([]) is 0 and Number([1]) is 1.