Skip to content
JavaScript js fundamentals 4 min read

Type Conversion & Coercion

JavaScript freely changes a value’s type to make an operation work, and that single design decision is behind both its convenience and its most notorious bugs. There are two ways types change: explicit conversion, where you deliberately call Number(), String(), or Boolean(), and implicit coercion, where the engine converts for you inside operators like +, ==, and conditionals. Understanding the rules — and choosing explicit conversion plus === — is what separates predictable code from mysterious one-off failures.

Explicit conversion

Explicit conversion means you call a conversion function yourself, so the result is obvious to anyone reading the code. The three core conversion functions are Number(), String(), and Boolean().

console.log(Number("42"));     // string -> number
console.log(String(42));       // number -> string
console.log(Boolean(0));       // number -> boolean
console.log(Number(""));       // empty string -> 0
console.log(Number("12px"));   // not fully numeric -> NaN

Output:

42
42
false
0
NaN

For parsing user input, prefer parseInt() and parseFloat(), which read leading numeric characters and stop at the first invalid one — Number() is all-or-nothing.

console.log(parseInt("12px", 10)); // 12  (always pass the radix)
console.log(parseFloat("3.14em")); // 3.14
console.log(Number("12px"));       // NaN

Output:

12
3.14
NaN

Tip: always pass the radix to parseInt(value, 10). Without it, older inputs like leading-zero strings can be misread, and being explicit removes all ambiguity.

Truthy and falsy values

Every value has an inherent boolean meaning used in conditionals, &&, ||, and !. There are exactly eight falsy values; everything else is truthy.

Falsy valuesTruthy examples
falsetrue
0, -0any non-zero number
0n (BigInt zero)"0", "false" (non-empty strings)
"" (empty string)[] (empty array)
null{} (empty object)
undefinedany function
NaNInfinity

The two that surprise people most: "0" and [] are both truthy, because they are a non-empty string and an object respectively.

if ([]) console.log("empty array is truthy");
if ("0") console.log("the string zero is truthy");
console.log(Boolean(""), Boolean(" ")); // empty vs. a space

Output:

empty array is truthy
the string zero is truthy
false true

Implicit coercion in operators

The + operator is overloaded: if either operand is a string, it does string concatenation; otherwise it does numeric addition. Every other arithmetic operator (-, *, /, %) always coerces to number.

console.log(1 + "2");   // "12"  — string wins
console.log("5" - 1);   // 4     — subtraction forces number
console.log("5" * "2"); // 10    — both coerced to number
console.log(1 + 2 + "3"); // "33" — left to right
console.log([] + {});   // "[object Object]"

Output:

12
4
10
33
[object Object]

The == coercion table

Loose equality == converts operands to a common type before comparing, which produces results that are hard to predict. Strict equality === never converts and is almost always what you want.

Expression== resultWhy
1 == "1"truestring coerced to number
0 == ""trueboth coerce to 0
0 == "0"trueboth coerce to 0
"" == "0"falsetwo strings, compared directly
null == undefinedtruespecial-cased to equal each other
null == 0falsenull only loosely equals undefined
NaN == NaNfalseNaN is never equal to anything
[] == falsetrue[] -> "" -> 0, false -> 0
console.log(0 == "");          // true
console.log(0 === "");         // false
console.log(null == undefined); // true
console.log(null === undefined); // false

Output:

true
false
true
false

Warning: the only defensible use of == is value == null, a concise check that matches both null and undefined. Everywhere else, use ===.

NaN: the value that isn’t equal to itself

NaN (“Not a Number”) is the result of an invalid numeric operation, and it is the only value in JavaScript not equal to itself. That means === cannot detect it — use Number.isNaN() instead.

const result = Number("oops");
console.log(result);             // NaN
console.log(result === NaN);     // false (!)
console.log(Number.isNaN(result)); // true

// Older global isNaN() coerces first, so it lies:
console.log(isNaN("hello"));        // true  — coerced to NaN
console.log(Number.isNaN("hello")); // false — not actually NaN

Output:

NaN
false
true
true
false

Prefer Number.isNaN() over the legacy global isNaN(), which coerces its argument first and so reports true for any non-numeric string.

Best Practices

  • Convert deliberately with Number(), String(), and Boolean() so your intent is visible at the call site.
  • Always use === and !==; reserve == exclusively for the value == null idiom.
  • Pass a radix to parseInt(str, 10), and prefer parseInt/parseFloat for parsing strings with trailing units.
  • Memorize the eight falsy values — and remember "0" and [] are truthy.
  • Detect NaN with Number.isNaN(), never with === or the coercing global isNaN().
  • Avoid relying on + for type changes; String(x) is clearer than x + "" and Number(x) clearer than +x.
Last updated June 1, 2026
Was this helpful?