How to Convert Data Types in JavaScript: Explicit and Implicit Type Conversions
JavaScript is dynamically typed, which means values can change from one type to another, sometimes deliberately, sometimes without you realizing it. When you concatenate a number with a string, compare a string with a number, or pass a value into an if condition, JavaScript silently converts types behind the scenes. These automatic conversions follow specific rules, and not knowing them leads to some of the most confusing bugs in the language.
This guide covers both explicit conversion (when you intentionally convert a value) and implicit coercion (when JavaScript converts automatically). You will learn every rule, see exactly what each conversion produces, and understand why expressions like "" == 0 evaluate to true.
Why Type Conversion Matters in JavaScript
In strongly typed languages like Python or Java, mixing types causes an immediate error. JavaScript takes a different approach: it tries to make things work by converting values to compatible types. This makes the language flexible but unpredictable if you do not understand the conversion rules.
// JavaScript converts silently: sometimes correctly, sometimes not
console.log("5" + 3); // "53" (number → string, concatenation)
console.log("5" - 3); // 2 (string → number, subtraction)
console.log("5" * "3"); // 15 (both strings → numbers)
console.log(true + true); // 2 (booleans → numbers)
console.log([] + []); // "" (arrays → strings, concatenated)
console.log([] + {}); // "[object Object]"
console.log({} + []); // 0 or "[object Object]" depending on context!
Every one of these results follows a specific set of rules. By the end of this guide, you will be able to predict the output of any type conversion.
Three Types of Conversion
All type conversions in JavaScript ultimately fall into three categories:
- To String when a value needs to be displayed or concatenated with text
- To Number when a value needs to participate in arithmetic
- To Boolean when a value needs to be evaluated as true/false
String Conversion
String conversion happens when you need the text representation of a value. There are several ways to convert explicitly, and each has slightly different behavior.
String(): The Universal Converter
The String() function converts any value to its string representation:
console.log(String(42)); // "42"
console.log(String(3.14)); // "3.14"
console.log(String(true)); // "true"
console.log(String(false)); // "false"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"
console.log(String(NaN)); // "NaN"
console.log(String(Infinity)); // "Infinity"
console.log(String(0n)); // "0"
console.log(String(Symbol("x"))); // "Symbol(x)"
String() works on every value, including null and undefined, which makes it the safest option.
.toString(): The Method Approach
Most values have a .toString() method, but null and undefined do not:
let num = 42;
console.log(num.toString()); // "42"
console.log(true.toString()); // "true"
console.log([1, 2, 3].toString()); // "1,2,3"
// Numbers can convert to different bases
console.log((255).toString(16)); // "ff" (hexadecimal)
console.log((255).toString(2)); // "11111111" (binary)
console.log((255).toString(8)); // "377" (octal)
// null and undefined crash
null.toString(); // TypeError: Cannot read properties of null
undefined.toString(); // TypeError: Cannot read properties of undefined
Use String() instead of .toString() when the value might be null or undefined. String(null) returns "null" safely, while null.toString() throws a TypeError.
Template Literals: Implicit String Conversion
Template literals automatically convert embedded expressions to strings:
let age = 30;
let message = `Age: ${age}`;
console.log(message); // "Age: 30"
let isActive = true;
console.log(`Status: ${isActive}`); // "Status: true"
console.log(`Value: ${null}`); // "Value: null"
console.log(`Value: ${undefined}`); // "Value: undefined"
console.log(`Value: ${[1, 2, 3]}`); // "Value: 1,2,3"
Concatenation with + Operator
When the + operator encounters a string, it converts the other operand to a string:
console.log("Score: " + 100); // "Score: 100"
console.log("" + 42); // "42"
console.log("" + true); // "true"
console.log("" + null); // "null"
console.log("" + undefined); // "undefined"
Using "" + value is a common shorthand for converting to string, though String(value) is more explicit and readable.
Complete String Conversion Table
| Original Value | String() Result |
|---|---|
42 | "42" |
0 | "0" |
-0 | "0" |
3.14 | "3.14" |
NaN | "NaN" |
Infinity | "Infinity" |
-Infinity | "-Infinity" |
0n | "0" |
true | "true" |
false | "false" |
null | "null" |
undefined | "undefined" |
"" | "" |
[] | "" |
[1, 2] | "1,2" |
{} | "[object Object]" |
Symbol("x") | "Symbol(x)" |
Numeric Conversion
Numeric conversion happens in mathematical operations and comparisons. JavaScript provides several ways to convert values to numbers, each with its own parsing behavior.
Number(): Strict Conversion
Number() converts the entire value to a number. If the value cannot be fully converted, it returns NaN:
console.log(Number("42")); // 42
console.log(Number("3.14")); // 3.14
console.log(Number("")); // 0 ← important!
console.log(Number(" ")); // 0 ← whitespace-only strings → 0
console.log(Number(" 42 ")); // 42 ← trims whitespace
console.log(Number("42abc")); // NaN ← cannot convert entire string
console.log(Number("abc")); // NaN
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null)); // 0 ← important!
console.log(Number(undefined)); // NaN ← important!
console.log(Number([])) // 0 ← empty array
console.log(Number([5])); // 5 ← single-element array
console.log(Number([1, 2])); // NaN ← multi-element array
parseInt(): Integer Parsing
parseInt() reads a string character by character and stops when it finds a non-numeric character. It extracts the integer part:
console.log(parseInt("42px")); // 42 ← stops at "p"
console.log(parseInt("3.14")); // 3 ← stops at "."
console.log(parseInt("100abc200")); // 100 ← stops at "a"
console.log(parseInt(" 42 ")); // 42 ← trims leading whitespace
console.log(parseInt("abc42")); // NaN ← starts with non-numeric
console.log(parseInt("")); // NaN ← different from Number("")!
console.log(parseInt("0xFF")); // 255 ← recognizes hex prefix
parseInt() accepts a second argument specifying the radix (base):
console.log(parseInt("ff", 16)); // 255 (hexadecimal)
console.log(parseInt("111", 2)); // 7 (binary)
console.log(parseInt("77", 8)); // 63 (octal)
console.log(parseInt("42", 10)); // 42 (decimal, explicit)
Always pass the radix when using parseInt(). Without it, parseInt() may guess the base incorrectly in edge cases:
// Always specify base 10 for decimal numbers
parseInt("08"); // 8 (correct in modern browsers, but was 0 in old ones!)
parseInt("08", 10); // 8 (always correct)
parseFloat(): Float Parsing
parseFloat() works like parseInt() but also reads decimal points:
console.log(parseFloat("3.14")); // 3.14
console.log(parseFloat("3.14.159")); // 3.14 ← stops at second dot
console.log(parseFloat("42px")); // 42
console.log(parseFloat(".5")); // 0.5
console.log(parseFloat("abc")); // NaN
console.log(parseFloat("")); // NaN
Unary + Operator (Shorthand)
The unary + operator works identically to Number():
console.log(+"42"); // 42
console.log(+"3.14"); // 3.14
console.log(+""); // 0
console.log(+true); // 1
console.log(+false); // 0
console.log(+null); // 0
console.log(+undefined); // NaN
console.log(+"abc"); // NaN
This is commonly used to convert prompt() results (which are always strings) to numbers:
let input = prompt("Enter a number:");
let num = +input; // Quick conversion using unary +
// Equivalent to:
let num = Number(input);
Comparing Conversion Methods
| Input | Number() | parseInt() | parseFloat() | Unary + |
|---|---|---|---|---|
"42" | 42 | 42 | 42 | 42 |
"3.14" | 3.14 | 3 | 3.14 | 3.14 |
"42px" | NaN | 42 | 42 | NaN |
"" | 0 | NaN | NaN | 0 |
" " | 0 | NaN | NaN | 0 |
"0xFF" | 255 | 255 | 0 | 255 |
true | 1 | NaN | NaN | 1 |
null | 0 | NaN | NaN | 0 |
undefined | NaN | NaN | NaN | NaN |
When to use each:
Number()or unary+: When you want strict conversion (the entire string must be a valid number)parseInt(): When you need to extract an integer from a string that may contain non-numeric suffixes (like"42px")parseFloat(): Same asparseInt()but when you need decimal precision
Complete Numeric Conversion Table
| Original Value | Number() Result | Notes |
|---|---|---|
"0" | 0 | |
"42" | 42 | |
"3.14" | 3.14 | |
"" (empty string) | 0 | Counterintuitive! |
" " (spaces) | 0 | Whitespace-only → 0 |
"\t\n" | 0 | Any whitespace → 0 |
"42abc" | NaN | Partial numbers fail |
"abc" | NaN | |
true | 1 | |
false | 0 | |
null | 0 | Counterintuitive! |
undefined | NaN | Different from null! |
[] | 0 | [] → "" → 0 |
[5] | 5 | [5] → "5" → 5 |
[1, 2] | NaN | [1,2] → "1,2" → NaN |
{} | NaN | |
NaN | NaN | |
Infinity | Infinity |
Boolean Conversion: The Complete Truthy/Falsy Table
Boolean conversion is the simplest of the three, but it contains some of the most surprising edge cases in JavaScript.
The Rule
The rule is straightforward. There are exactly eight falsy values. Everything else is truthy.
Falsy Values (Convert to false)
console.log(Boolean(false)); // false (the value false itself)
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(0n)); // false (BigInt zero)
console.log(Boolean("")); // false (empty string only)
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
Truthy Values (Convert to true)
Everything that is not in the falsy list is truthy. This includes many values that look like they should be falsy:
// Obviously truthy
console.log(Boolean(42)); // true
console.log(Boolean("hello")); // true
console.log(Boolean(true)); // true
// Surprisingly truthy: memorize these!
console.log(Boolean("0")); // true ← non-empty string!
console.log(Boolean(" ")); // true ← non-empty string (contains space)!
console.log(Boolean("false")); // true ← non-empty string!
console.log(Boolean("null")); // true ← non-empty string!
console.log(Boolean("undefined")); // true ← non-empty string!
console.log(Boolean([])); // true ← empty array is truthy!
console.log(Boolean({})); // true ← empty object is truthy!
console.log(Boolean(function(){})); // true
console.log(Boolean(-1)); // true
console.log(Boolean(Infinity)); // true
console.log(Boolean(-Infinity)); // true
console.log(Boolean(new Date())); // true
console.log(Boolean(0.1)); // true
The three most dangerous truthy values for beginners:
"0"is truthy (it is a non-empty string, not the number0)" "is truthy (it is a non-empty string containing a space)[]is truthy (it is an object, and all objects are truthy)
These three catch developers off guard constantly. If you remember nothing else about boolean conversion, remember these.
Where Boolean Conversion Happens Automatically
JavaScript converts values to booleans in these contexts:
// 1. if/else conditions
if ("hello") { } // true (runs)
if ("") { } // false (skipped)
if (0) { } // false (skipped)
if ([]) { } // true (runs) (empty array is truthy!)
// 2. Logical operators
console.log(!"hello"); // false (truthy → negated to false)
console.log(!0); // true (falsy → negated to true)
console.log(!!"hello"); // true (double negation → boolean)
// 3. Ternary operator
let status = "" ? "active" : "inactive"; // "inactive"
// 4. while/for conditions
while (remainingItems) { } // runs while remainingItems is truthy
Double NOT (!!) for Explicit Boolean Conversion
The double NOT operator is a common shorthand for Boolean():
console.log(!!"hello"); // true -> same as Boolean("hello")
console.log(!!0); // false -> same as Boolean(0)
console.log(!!null); // false -> same as Boolean(null)
console.log(!!""); // false -> same as Boolean("")
console.log(!![]); // true -> same as Boolean([])
Both !!value and Boolean(value) produce the same result. Boolean() is more readable; !! is more concise. Use whichever your team prefers.
Implicit (Automatic) Type Coercion
Implicit coercion is what makes JavaScript interviews challenging and debugging frustrating. It happens automatically when operators or language constructs encounter values of unexpected types.
String Coercion with the + Operator
The + operator has dual behavior: it performs addition with numbers and concatenation with strings. When one operand is a string, the other is converted to a string:
// String + anything = string concatenation
console.log("5" + 3); // "53" -> 3 becomes "3"
console.log("5" + true); // "5true"
console.log("5" + null); // "5null"
console.log("5" + undefined); // "5undefined"
console.log("5" + [1, 2]); // "51,2"
// Order matters: evaluated left to right
console.log(1 + 2 + "3"); // "33" -> (1+2) = 3, then 3+"3" = "33"
console.log("1" + 2 + 3); // "123" -> "1"+2 = "12", then "12"+3 = "123"
Numeric Coercion with Other Operators
All other mathematical operators (-, *, /, %, **) convert operands to numbers:
console.log("6" - 2); // 4
console.log("6" * "3"); // 18
console.log("6" / "2"); // 3
console.log("10" % "3"); // 1
console.log("2" ** "3"); // 8
console.log(true + true); // 2 (1 + 1)
console.log(true + false); // 1 (1 + 0)
console.log(false - true); // -1 (0 - 1)
// The difference between + and -
console.log("5" + 3); // "53" (concatenation)
console.log("5" - 3); // 2 (subtraction, forces numeric conversion)
Numeric Coercion in Comparisons
The comparison operators >, <, >=, <= convert operands to numbers:
console.log("10" > 9); // true -> "10" → 10, then 10 > 9
console.log("01" == 1); // true -> "01" → 1
console.log(true > 0); // true -> true → 1, then 1 > 0
console.log(false == 0); // true -> false → 0
console.log("" == 0); // true -> "" → 0
Boolean Coercion in Logical Contexts
Values are converted to booleans when used with if, while, !, &&, ||, and ? ::
if ("hello") { } // string → true → block runs
if (0) { } // number → false → block skipped
let x = "" || "default"; // "" → false → returns "default"
let y = "value" && 42; // "value" → true → returns 42
The Coercion Chain
Sometimes JavaScript performs multiple conversions in sequence. Understanding the chain reveals why certain expressions produce unexpected results:
// Step by step: [] + []
// 1. [] needs to become a primitive → [].toString() → ""
// 2. [] needs to become a primitive → [].toString() → ""
// 3. "" + "" → ""
console.log([] + []); // ""
// Step by step: [] + {}
// 1. [] → ""
// 2. {} → "[object Object]"
// 3. "" + "[object Object]" → "[object Object]"
console.log([] + {}); // "[object Object]"
// Step by step: true + "5"
// 1. One operand is a string, so + does concatenation
// 2. true → "true"
// 3. "true" + "5" → "true5"
console.log(true + "5"); // "true5"
// Step by step: true + 5
// 1. Neither is a string, so + does addition
// 2. true → 1
// 3. 1 + 5 → 6
console.log(true + 5); // 6
The Abstract Equality Algorithm (Why == Is Dangerous)
The loose equality operator == performs type coercion before comparing. The rules it follows are called the Abstract Equality Comparison Algorithm, and they are the single biggest source of "WTF JavaScript" moments.
How == Works Step by Step
When the types are different, == follows this algorithm:
- null == undefined →
true(special rule) - Number == String → convert string to number, then compare
- Boolean == Anything → convert boolean to number first, then compare
- Object == Primitive → convert object to primitive, then compare
Examples with Explanation
// Rule 1: null and undefined are loosely equal to each other (and nothing else)
console.log(null == undefined); // true (special rule)
console.log(null == 0); // false
console.log(null == ""); // false
console.log(null == false); // false
console.log(undefined == 0); // false
console.log(undefined == ""); // false
console.log(undefined == false); // false
// Rule 2: Number == String → String converted to Number
console.log(1 == "1"); // true -> "1" → 1, then 1 == 1
console.log(0 == ""); // true -> "" → 0, then 0 == 0
console.log(0 == "0"); // true -> "0" → 0, then 0 == 0
console.log(42 == "42"); // true -> "42" → 42
console.log(0 == "abc"); // false -> "abc" → NaN, then 0 == NaN is false
// Rule 3: Boolean == Anything → Boolean converted to Number first
console.log(true == 1); // true -> true → 1, then 1 == 1
console.log(true == "1"); // true -> true → 1, "1" → 1, then 1 == 1
console.log(false == 0); // true -> false → 0, then 0 == 0
console.log(false == ""); // true -> false → 0, "" → 0, then 0 == 0
console.log(false == "0"); // true -> false → 0, "0" → 0, then 0 == 0
// This is why == is dangerous:
console.log(false == ""); // true -> makes no logical sense!
console.log(false == "0"); // true -> even worse!
console.log("" == "0"); // false -> but these two are both "falsy"!
The Infamous Inconsistency
Here is the classic example that demonstrates why == is unreliable:
console.log(false == "0"); // true
console.log(false == ""); // true
console.log("0" == ""); // false ← Wait, what?!
// If false == "0" and false == "", you'd expect "0" == "" to be true
// But it's false because when both are strings, no conversion happens
// "0" and "" are different strings
This violates the mathematical property of transitivity (if A == B and B == C, then A == C). The == operator is not transitive, which makes it fundamentally unpredictable in complex conditions.
The Solution: Always Use ===
The strict equality operator === does no type conversion. If the types are different, it immediately returns false:
console.log(1 === "1"); // false (different types)
console.log(0 === ""); // false (different types)
console.log(0 === false); // false (different types)
console.log(null === undefined); // false (different types)
console.log(true === 1); // false (different types)
// Only true when both type AND value match
console.log(1 === 1); // true
console.log("hello" === "hello"); // true
The golden rule: always use === and !==. The only exception is checking for null or undefined together, where value == null is an accepted shorthand for value === null || value === undefined.
Common Mistakes with Type Conversions
Mistake 1: "0" Is Truthy But 0 Is Falsy
let input = "0"; // String from prompt() or form input
if (input) {
console.log("This runs!"); // "0" is truthy because it's a non-empty string
}
if (Number(input)) {
console.log("This does NOT run"); // Number("0") is 0, which is falsy
}
This is especially dangerous with form inputs and prompt(), which always return strings:
let quantity = prompt("How many items?"); // User types "0"
// BAD: checking the string
if (quantity) {
processOrder(quantity); // Runs even when quantity is "0"!
}
// GOOD: convert to number first, then check
let qty = Number(quantity);
if (qty > 0) {
processOrder(qty); // Only runs for positive quantities
}
Mistake 2: Empty String Converts to 0
console.log(Number("")); // 0 (empty string becomes zero!)
console.log(Number(" ")); // 0 (whitespace-only string also becomes zero!)
// This leads to unexpected equality
console.log(0 == ""); // true ("" → 0, then 0 == 0)
console.log(0 == " "); // true (" " → 0, then 0 == 0)
// Fix: use strict equality
console.log(0 === ""); // false
console.log(0 === " "); // false
Mistake 3: null Converts to 0 in Arithmetic But Not in Comparisons
// In arithmetic: null → 0
console.log(null + 5); // 5
console.log(null * 10); // 0
// In equality comparison: null has special rules
console.log(null == 0); // false ← null is NOT converted to 0 with ==
console.log(null > 0); // false ← null → 0, and 0 > 0 is false
console.log(null >= 0); // true ← null → 0, and 0 >= 0 is true!
console.log(null < 1); // true ← null → 0, and 0 < 1 is true
// So null is >= 0 but not == 0 and not > 0. This is inconsistent!
The reason: == has its own special rules for null (it only equals undefined), while comparison operators (>, <, >=, <=) convert null to 0 using Number().
Mistake 4: undefined Becomes NaN
console.log(Number(undefined)); // NaN
console.log(undefined + 5); // NaN
console.log(undefined == 0); // false
console.log(undefined > 0); // false (NaN > 0 is false)
console.log(undefined < 0); // false (NaN < 0 is false)
console.log(undefined == null); // true (special rule)
NaN is not equal to anything, not greater than anything, and not less than anything. Any comparison involving NaN returns false (except != and !==).
Mistake 5: The + Operator Ambiguity
let a = "5";
let b = 3;
// Developer expects addition, gets concatenation
let result = a + b;
console.log(result); // "53" (oops!)
// Fix: convert first
let result = Number(a) + b;
console.log(result); // 8
// Or use unary +
let result = +a + b;
console.log(result); // 8
This is extremely common when working with form inputs, prompt() values, or data from APIs that return numbers as strings.
Mistake 6: Relying on == for Type-Sensitive Logic
// A real-world bug
function processValue(val) {
if (val == false) {
console.log("Received falsy value");
} else {
console.log("Received truthy value:", val);
}
}
processValue(0); // "Received falsy value" ← correct?
processValue(""); // "Received falsy value" ← did we want this?
processValue("0"); // "Received falsy value" ← "0" == false is true!
processValue([]); // "Received falsy value" ← [] == false is true!
processValue(null); // "Received truthy value: null" ← null == false is false!
The results are inconsistent and counterintuitive. An empty array matches false, but null does not, even though null is logically "more false" than an empty array.
Fix: Be explicit about what you are checking:
function processValue(val) {
if (val === false) {
console.log("Received exactly false");
} else if (val === 0) {
console.log("Received exactly zero");
} else if (val === "") {
console.log("Received empty string");
} else if (val == null) {
console.log("Received null or undefined");
} else {
console.log("Received:", val);
}
}
Quick Reference: Surprising Conversions
| Expression | Result | Why |
|---|---|---|
"0" == false | true | false → 0, "0" → 0, 0 == 0 |
"" == false | true | false → 0, "" → 0, 0 == 0 |
" " == false | true | false → 0, " " → 0, 0 == 0 |
[] == false | true | [] → "" → 0, false → 0, 0 == 0 |
[] == 0 | true | [] → "" → 0, 0 == 0 |
"" == 0 | true | "" → 0, 0 == 0 |
null == false | false | null only equals undefined |
NaN == NaN | false | NaN equals nothing |
null == 0 | false | null only equals undefined |
Boolean("") | false | empty string is falsy |
Boolean("0") | true | non-empty string is truthy |
Boolean(" ") | true | non-empty string is truthy |
Boolean([]) | true | all objects are truthy |
Number("") | 0 | empty string → 0 |
Number(null) | 0 | null → 0 |
Number(undefined) | NaN | undefined → NaN |
Summary
Type conversion is one of JavaScript's most important (and most treacherous) features. Here is what you need to remember:
- String conversion uses
String(),.toString(), template literals, or the+operator with a string operand.String()is the safest because it handlesnullandundefined. - Numeric conversion uses
Number(),parseInt(),parseFloat(), or the unary+.Number()requires the entire string to be valid;parseInt()andparseFloat()parse from the beginning and stop at invalid characters. - Boolean conversion has exactly eight falsy values:
false,0,-0,0n,"",null,undefined, andNaN. Everything else, including"0"," ",[], and{}, is truthy. - Implicit coercion happens automatically with operators. The
+operator concatenates if either operand is a string; other math operators convert to numbers. - The
==operator uses the Abstract Equality Algorithm, which converts types in complex and often unintuitive ways. - Always use
===for comparisons. The only common exception isvalue == nullto check for bothnullandundefined. - Be especially careful with
prompt()and form inputs, which always return strings. Convert to numbers explicitly withNumber()or+before doing arithmetic.
Understanding these rules thoroughly will prevent an entire category of bugs that plague JavaScript developers at every experience level.