Skip to main content
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.

Two kinds of conversion

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.

Explicit conversion

To number

Number("42")       // 42
Number("3.14")     // 3.14
Number("")         // 0
Number("hello")    // NaN
Number(true)       // 1
Number(false)      // 0
Number(null)       // 0
Number(undefined)  // NaN
Number([])         // 0  ([] converts to "", then "" to 0)
Number([1])        // 1
Number([1, 2])     // NaN
You can also use the unary + operator as a shorthand for Number():
+"42"   // 42
+""     // 0
+true   // 1

To string

String(42)         // "42"
String(true)       // "true"
String(null)       // "null"
String(undefined)  // "undefined"
String([1, 2, 3])  // "1,2,3"
Template literals and the .toString() method also trigger string conversion:
const n = 255;
n.toString()    // "255"
n.toString(16)  // "ff"  (hexadecimal)
n.toString(2)   // "11111111"  (binary)

To boolean

Every value in JavaScript is either truthy or falsy. There are exactly eight falsy values:
ValueType
falseBoolean
0Number
-0Number
0nBigInt
"" (empty string)String
nullNull
undefinedUndefined
NaNNumber
Everything that is not on the falsy list is truthy — including "0" (a non-empty string), [] (an empty array), and {} (an empty object).
Boolean(0)         // false
Boolean("")        // false
Boolean(null)      // false
Boolean(undefined) // false
Boolean(NaN)       // false
Boolean("0")       // true  — non-empty string
Boolean([])        // true  — objects are always truthy
Boolean({})        // true

Implicit conversion

The + operator

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 → concatenation
1 + "2"         // "12"   — number + string → concatenation
1 + 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 1
false + 1       // 1      — false coerced to 0
null + 1        // 1      — null coerced to 0
undefined + 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.

Arithmetic operators other than +

The -, *, /, and % operators always attempt numeric conversion. They do not have a string mode, so they coerce both operands to numbers:
"5" - 2    // 3
"5" * "2"  // 10
"6" / "2"  // 3
"5" - "x"  // NaN
true - 1   // 0

Comparison operators

The <, >, <=, and >= operators convert both operands to numbers if neither is a string. If both are strings, they are compared lexicographically.
"10" > 9    // true  — "10" converted to 10
"10" > "9"  // false — lexicographic: "1" < "9"
null > 0    // false
null == 0   // false
null >= 0   // true  — null converted to 0 for >=, but == has special rules

== vs ===

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 coercion
1 === 1        // true
1 === "1"      // false — different types
null === undefined // false

// Loose equality — with coercion
1 == "1"       // true  — "1" converted to 1
0 == false     // true  — false converted to 0
0 == ""        // true  — "" converted to 0
null == undefined // true  — special rule
null == 0      // false — null only == undefined
NaN == 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 (true1, false0) 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.

Object-to-primitive conversion

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.

Summary of conversion rules

// Key ToNumber conversions
Number(null)      // 0
Number(undefined) // NaN
Number(true)      // 1
Number(false)     // 0
Number("")        // 0
Number("  42  ")  // 42  (whitespace trimmed)
Number("42abc")   // NaN