Skip to main content

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:

  1. To String when a value needs to be displayed or concatenated with text
  2. To Number when a value needs to participate in arithmetic
  3. 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
caution

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 ValueString() 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)
warning

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

InputNumber()parseInt()parseFloat()Unary +
"42"42424242
"3.14"3.1433.143.14
"42px"NaN4242NaN
""0NaNNaN0
" "0NaNNaN0
"0xFF"2552550255
true1NaNNaN1
null0NaNNaN0
undefinedNaNNaNNaNNaN

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 as parseInt() but when you need decimal precision

Complete Numeric Conversion Table

Original ValueNumber() ResultNotes
"0"0
"42"42
"3.14"3.14
"" (empty string)0Counterintuitive!
" " (spaces)0Whitespace-only → 0
"\t\n"0Any whitespace → 0
"42abc"NaNPartial numbers fail
"abc"NaN
true1
false0
null0Counterintuitive!
undefinedNaNDifferent from null!
[]0[]""0
[5]5[5]"5"5
[1, 2]NaN[1,2]"1,2"NaN
{}NaN
NaNNaN
InfinityInfinity

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
danger

The three most dangerous truthy values for beginners:

  1. "0" is truthy (it is a non-empty string, not the number 0)
  2. " " is truthy (it is a non-empty string containing a space)
  3. [] 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:

  1. null == undefinedtrue (special rule)
  2. Number == String → convert string to number, then compare
  3. Boolean == Anything → convert boolean to number first, then compare
  4. 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
tip

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

ExpressionResultWhy
"0" == falsetruefalse → 0, "0" → 0, 0 == 0
"" == falsetruefalse → 0, "" → 0, 0 == 0
" " == falsetruefalse → 0, " " → 0, 0 == 0
[] == falsetrue[] → "" → 0, false → 0, 0 == 0
[] == 0true[] → "" → 0, 0 == 0
"" == 0true"" → 0, 0 == 0
null == falsefalsenull only equals undefined
NaN == NaNfalseNaN equals nothing
null == 0falsenull only equals undefined
Boolean("")falseempty string is falsy
Boolean("0")truenon-empty string is truthy
Boolean(" ")truenon-empty string is truthy
Boolean([])trueall objects are truthy
Number("")0empty string → 0
Number(null)0null → 0
Number(undefined)NaNundefined → 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 handles null and undefined.
  • Numeric conversion uses Number(), parseInt(), parseFloat(), or the unary +. Number() requires the entire string to be valid; parseInt() and parseFloat() parse from the beginning and stop at invalid characters.
  • Boolean conversion has exactly eight falsy values: false, 0, -0, 0n, "", null, undefined, and NaN. 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 is value == null to check for both null and undefined.
  • Be especially careful with prompt() and form inputs, which always return strings. Convert to numbers explicitly with Number() or + before doing arithmetic.

Understanding these rules thoroughly will prevent an entire category of bugs that plague JavaScript developers at every experience level.