JavaScript Type Conversion & Coercion Explained
Master JavaScript type conversion and coercion with practical examples. Learn how JavaScript automatically converts types, when to use explicit conversion, and how to avoid coercion-related bugs.
JavaScript is famous (and sometimes infamous) for converting values between types automatically. When you write "5" + 3 and get "53" instead of 8, that is type coercion in action. Understanding how JavaScript converts types is essential for writing predictable code, debugging unexpected behavior, and passing technical interviews where coercion questions are a staple.
This guide covers both type coercion (automatic, implicit conversion by the engine) and type conversion (explicit, intentional conversion by the developer). You will learn the rules that govern each, see practical examples of when they help or hurt, and build habits that keep your code predictable.
Type Conversion vs Type Coercion
These terms describe the same process (changing a value from one type to another) but differ in who initiates it:
| Term | Who Triggers It | Example | Result |
|---|---|---|---|
| Type Conversion (explicit) | Developer writes conversion code | Number("42") | 42 |
| Type Coercion (implicit) | JavaScript engine does it automatically | "42" - 0 | 42 |
Both terms fall under the umbrella of "type casting." The JavaScript data types article covers what each type is. This article focuses on how types transform into each other.
The Three Conversion Targets
JavaScript converts values in three directions: to String, to Number, and to Boolean. Every conversion in the language falls into one of these three categories.
Converting to String
Explicit string conversion uses String() or template literal:
console.log(String(42)); // "42"
console.log(String(true)); // "true"
console.log(String(false)); // "false"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"
console.log(String([1, 2, 3])); // "1,2,3"
console.log(String({ a: 1 })); // "[object Object]"
// Template literal conversion
const count = 5;
console.log(`Items: ${count}`); // "Items: 5" (number coerced to string)Implicit string coercion happens when the + operator encounters a string on either side:
console.log("The answer is " + 42); // "The answer is 42"
console.log(42 + " is the answer"); // "42 is the answer"
console.log("" + true); // "true"
console.log("" + null); // "null"
console.log("" + undefined); // "undefined"The + Operator Trap
The + operator is the primary source of coercion bugs. With two numbers, it adds. With a string on either side, it concatenates. With mixed types, the non-string is converted to a string. Always use Number() or parseInt() to ensure numeric addition when working with user input.
Converting to Number
Explicit number conversion uses Number(), parseInt(), or parseFloat():
console.log(Number("42")); // 42
console.log(Number("3.14")); // 3.14
console.log(Number("")); // 0
console.log(Number(" ")); // 0
console.log(Number("42px")); // NaN
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
// parseInt and parseFloat are more lenient
console.log(parseInt("42px")); // 42 (stops at non-numeric character)
console.log(parseFloat("3.14em")); // 3.14
console.log(parseInt("0xFF", 16)); // 255| Input Value | Number() | parseInt() | parseFloat() |
|---|---|---|---|
"42" | 42 | 42 | 42 |
"42px" | NaN | 42 | 42 |
"3.14" | 3.14 | 3 | 3.14 |
"" | 0 | NaN | NaN |
" " | 0 | NaN | NaN |
true | 1 | NaN | NaN |
null | 0 | NaN | NaN |
undefined | NaN | NaN | NaN |
Implicit number coercion happens with arithmetic operator (except + with strings), comparison operators, and the unary +:
console.log("6" - 2); // 4 (string coerced to number)
console.log("6" * 2); // 12
console.log("6" / 2); // 3
console.log(true + 1); // 2 (true becomes 1)
console.log(false + 1); // 1 (false becomes 0)
console.log(+"42"); // 42 (unary plus converts to number)
console.log(+true); // 1
console.log(+false); // 0
console.log(+""); // 0
console.log(+null); // 0
console.log(+undefined); // NaNConverting to Boolean
Explicit boolean conversion uses Boolean() or the double-NOT operator !!:
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(false)); // false
console.log(Boolean(1)); // true
console.log(Boolean("hello")); // true
console.log(Boolean([])); // true (empty array is truthy!)
console.log(Boolean({})); // true (empty object is truthy!)
console.log(Boolean("0")); // true (non-empty string is truthy!)
// Double-NOT shorthand
console.log(!!0); // false
console.log(!!"hello"); // true
console.log(!!null); // falseThere are exactly six falsy values in JavaScript: false, 0 (and -0, 0n), "" (empty string), null, undefined, and NaN. Everything else is truthy, including empty objects, empty arrays, and the string "0".
Implicit boolean coercion happens in if statements, logical operators, and the ternary operator:
const username = "";
if (username) {
console.log("User exists");
} else {
console.log("No user"); // This runs (empty string is falsy)
}
// Logical OR for defaults
const displayName = username || "Anonymous"; // "Anonymous"
// Nullish coalescing (does NOT coerce to boolean)
const value = 0;
const result1 = value || 10; // 10 (0 is falsy)
const result2 = value ?? 10; // 0 (?? only checks null/undefined)Coercion Rules That Trip Up Developers
Some coercion results are counter-intuitive. Here are the ones that appear most frequently in bugs and interview questions:
// The + operator inconsistency
console.log(1 + "2"); // "12" (number + string = string concatenation)
console.log(1 - "2"); // -1 (number - string = numeric subtraction)
// null and undefined in arithmetic
console.log(null + 1); // 1 (null becomes 0)
console.log(undefined + 1); // NaN (undefined becomes NaN)
// Loose equality surprises
console.log(0 == ""); // true (both coerce to 0)
console.log(0 == false); // true (both coerce to 0)
console.log("" == false); // true (both coerce to 0)
console.log(null == undefined); // true (special rule)
console.log(null == 0); // false (null only equals undefined)
console.log(NaN == NaN); // false (NaN is never equal to anything)
// Array coercion
console.log([] + []); // "" (both coerce to empty string)
console.log([] + {}); // "[object Object]"
console.log(+[]); // 0 ([] → "" → 0)
console.log(+[1]); // 1 ([1] → "1" → 1)
console.log(+[1, 2]); // NaN ([1,2] → "1,2" → NaN)Explicit Conversion Patterns for Real Code
Instead of relying on implicit coercion, use explicit conversion to make your intent clear:
Form Input Processing
// User input from forms is always a string
const ageInput = document.querySelector("#age").value; // "25"
const priceInput = document.querySelector("#price").value; // "19.99"
// BAD: implicit coercion
const totalBad = priceInput * ageInput; // Works by accident
// GOOD: explicit conversion with validation
const age = parseInt(ageInput, 10);
const price = parseFloat(priceInput);
if (Number.isNaN(age) || Number.isNaN(price)) {
console.error("Invalid input: expected numbers");
} else {
const total = price * age;
console.log(`Total: $${total.toFixed(2)}`);
}API Response Handling
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
// API might return numbers as strings
return {
id: Number(data.id),
name: String(data.name),
isActive: Boolean(data.isActive),
balance: parseFloat(data.balance) || 0,
createdAt: new Date(data.createdAt),
};
}String to Number Conversion Methods Compared
const input = "42.5px";
console.log(Number(input)); // NaN (strict, rejects non-numeric chars)
console.log(parseInt(input, 10)); // 42 (parses until non-digit, drops decimal)
console.log(parseFloat(input)); // 42.5 (parses until non-numeric, keeps decimal)
console.log(+input); // NaN (same as Number())Best Practices
Write Predictable Type Conversions
These practices eliminate the most common coercion-related bugs in JavaScript projects.
Always use === instead of ==. Strict equality does not trigger type coercion. The result is predictable: values must be the same type AND the same value to be equal. This single rule eliminates an entire category of bugs.
Convert explicitly at input boundaries. Whenever you receive data from an external source (form fields, URL parameters, API responses, localStorage), convert it to the expected type immediately with Number(), String(), or Boolean().
Use Number.isNaN() instead of isNaN(). The global isNaN() function coerces its argument to a number first, so isNaN("hello") returns true. Number.isNaN() only returns true for the actual NaN value.
Prefer template literals over + for string building. Template literals (\`) handle type conversion explicitly and readably: `Total: $`is clearer than"Total: " + price`.
Use ?? instead of || for defaults when 0 or "" are valid values. The logical OR operator (||) treats 0, "", and false as falsy and falls through to the default. The nullish coalescing operator (??) only falls through on null or undefined.
Common Mistakes and How to Avoid Them
Coercion Pitfalls
These mistakes are responsible for a large percentage of JavaScript bugs in production applications.
Using + for arithmetic with string inputs. The + operator concatenates when either operand is a string. Convert inputs to numbers first with Number() or parseInt() before adding them.
Relying on loose equality (==) for comparisons. Loose equality applies a complex set of coercion rules that even experienced developers cannot predict reliably. Always use ===.
Checking for empty values with falsy checks. Writing if (!value) catches null and undefined, but also catches 0, "", and false. If any of those are valid values in your context, use explicit checks: if (value === null || value === undefined) or if (value == null) (the one acceptable use of ==).
Forgetting that parseInt needs a radix. Always pass the radix (base) as the second argument: parseInt("08", 10). Without it, older JavaScript engines might interpret leading zeros as octal numbers.
Treating NaN as a comparable value. NaN === NaN returns false. The only way to check for NaN is Number.isNaN(value) or Object.is(value, NaN).
Next Steps
Deep dive into implicit vs explicit conversion
Explore the implicit vs explicit type conversion guide for a detailed comparison of when each approach is appropriate.
Practice with operators
Apply type conversion knowledge to arithmetic, comparison, and logical operators with real-world examples.
Build a type-safe input validator
Create a form validation module that explicitly converts and validates every input field before processing.
Explore TypeScript strict mode
Try TypeScript's strict mode to see how compile-time type checking catches coercion bugs before they reach production.
Rune AI
Key Insights
- Three conversion targets: every type conversion in JavaScript produces a String, Number, or Boolean
- The + operator is the main trap: it concatenates with strings but adds with numbers, causing silent bugs with mixed types
- Always use === over ==: strict equality skips coercion entirely, making comparisons predictable
- Convert at input boundaries: use
Number(),String(), orBoolean()when receiving external data - Six values are falsy:
false,0,"",null,undefined, andNaN; everything else is truthy
Frequently Asked Questions
What is the difference between type conversion and type coercion in JavaScript?
Why does "5" + 3 return "53" but "5" - 3 returns 2?
How do I convert a string to a number safely in JavaScript?
Why does null == undefined return true in JavaScript?
What are falsy values in JavaScript and why do they matter?
Conclusion
Type conversion and coercion are fundamental mechanisms in JavaScript that determine how values transform during operations, comparisons, and control flow. The language converts values in three directions: to String, to Number, and to Boolean, with each direction following specific rules the engine applies automatically. Mastering these rules means understanding why "5" + 3 produces "53", why null + 1 produces 1, and why always using strict equality (===) with explicit conversions at input boundaries produces code that behaves exactly as intended.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.