JavaScript Type Coercion: Implicit Conversion Explained
Understand how JavaScript silently converts types with +, -, comparisons and boolean checks, so you can predict results and avoid sneaky bugs.
What you will learn
In the last lesson you changed types on purpose with Number(), String() and Boolean(). But JavaScript also changes types by itself, without asking you. This is called type coercion. It explains why "5" + 3 is "53" but "5" - 3 is 2, and why if ("hello") works. By the end of this lesson you will be able to predict the result of such code, and know how to stay in control.
Conversion vs coercion
Both words mean “changing a value from one type to another”. The difference is who does it.
| Who converts? | Example | |
|---|---|---|
| Explicit conversion (type casting) | You, using a function | Number("5") |
| Implicit conversion (type coercion) | JavaScript, automatically | "5" - 3 |
// Explicit: you decided
const a = Number("5") + 3;
console.log(a); // 8
// Implicit: JavaScript decided
const b = "5" - 3;
console.log(b); // 2
Why does coercion happen?
Operators expect certain types. The - operator only makes sense with numbers, so when you give it a string, JavaScript tries to turn that string into a number instead of throwing an error. It is helpful when it guesses right and confusing when it guesses differently from what you wanted.
JavaScript coerces values into one of only three types:
- String (when joining text)
- Number (when doing maths or comparing)
- Boolean (when making a yes/no decision)
Learn the rules for these three, and you understand coercion.
1. Coercion to string: the + operator
The + operator is special because it does two jobs: add numbers and join strings. The rule is simple:
If either side is a string,
+joins them as text. Otherwise it adds numbers.
console.log(5 + 3); // 8 (both numbers: add)
console.log("5" + 3); // "53" (one is a string: join)
console.log("Age: " + 20); // "Age: 20"
Left to right matters
JavaScript solves + one pair at a time, from left to right:
console.log(1 + 2 + "3"); // "33" (1+2 = 3 first, then "3" + "3")
console.log("1" + 2 + 3); // "123" (text from the start: everything joins)
console.log("Total: " + 5 + 5); // "Total: 55"
console.log("Total: " + (5 + 5)); // "Total: 10" (brackets fix it)
Whenever you mix text and numbers in a +, use brackets or a template literal to make your intention clear.
Other values turn into text too
When joined with a string, every value is converted to its text form:
console.log("Value: " + true); // "Value: true"
console.log("Value: " + null); // "Value: null"
console.log("Value: " + undefined); // "Value: undefined"
console.log("Items: " + [1, 2, 3]); // "Items: 1,2,3"
console.log("User: " + {}); // "User: [object Object]"
The last line is a common sign of a bug: you wanted to show an object, but JavaScript turned it into the useless text [object Object].
2. Coercion to number: -, *, /, %, **
Unlike +, these operators have only one job (maths), so they always convert both sides to numbers:
console.log("10" - 3); // 7
console.log("10" * "2"); // 20
console.log("20" / 4); // 5
console.log("7" % "4"); // 3
console.log("2" ** "3"); // 8
If the text cannot be understood as a number, the result is NaN (Not a Number):
console.log("hello" - 1); // NaN
console.log("12px" * 2); // NaN
How values become numbers
| Value | Becomes | Notes |
|---|---|---|
"42" |
42 |
Normal number text |
" 42 " |
42 |
Spaces around are ignored |
"3.14" |
3.14 |
|
"" (empty string) |
0 |
Surprise! |
" " (only spaces) |
0 |
|
"12px" |
NaN |
Letters spoil it |
true |
1 |
|
false |
0 |
|
null |
0 |
|
undefined |
NaN |
|
[] (empty array) |
0 |
|
[5] |
5 |
One-item array |
[1, 2] |
NaN |
console.log(true + 1); // 2
console.log(null + 1); // 1
console.log(undefined + 1); // NaN
console.log(true + true); // 2
Remember:
nullbecomes0, butundefinedbecomesNaN. This difference trips up many people.
The unary plus +
A single + placed in front of a value is a quick way to convert it to a number. It is the shortest form of Number():
console.log(+"42"); // 42
console.log(+"3.14"); // 3.14
console.log(+true); // 1
console.log(+""); // 0
console.log(+"abc"); // NaN
It is used a lot, but Number("42") is clearer for beginners to read.
3. Coercion in comparisons
Greater than and less than
With >, <, >= and <=:
- If one side is a number, the other side is converted to a number.
- If both sides are strings, they are compared letter by letter like in a dictionary (not as numbers).
console.log("10" > 5); // true (text becomes 10, then 10 > 5)
console.log(2 < "10"); // true
console.log("10" > "9"); // false! (both are text: "1" comes before "9")
console.log("apple" < "banana"); // true
console.log("a" < "B"); // false (small letters come after capital letters)
The "10" > "9" result is a classic bug when sorting or comparing numbers that came from inputs. Convert them first.
Equality: == vs ===
=== never coerces. If the types are different, the answer is false. == does coerce, using these rules:
| Case | Rule | Example |
|---|---|---|
| Same type | Compare normally | "a" == "a" is true |
| Number and string | String becomes a number | 5 == "5" is true |
| Boolean and anything | Boolean becomes a number (1 or 0) first |
true == "1" is true |
null and undefined |
Equal to each other, and to nothing else | null == undefined is true, null == 0 is false |
| Object and primitive | Object becomes a primitive | [1] == 1 is true |
console.log(5 == "5"); // true
console.log(true == "1"); // true
console.log("0" == false); // true
console.log("" == 0); // true
console.log(null == undefined); // true
console.log(null == 0); // false
console.log([] == false); // true
console.log(NaN == NaN); // false
Some of those are very unexpected, which is why you learned to use ===:
console.log(5 === "5"); // false
console.log("0" === false); // false
console.log([] === false); // false
Tip: The simple rule is: use
===and!==for equality. Then the equality coercion rules never matter, and you only need to understand them to read other people’s code.
4. Coercion to boolean: truthy and falsy
Anywhere JavaScript needs a yes or no answer, it converts the value to a boolean. These places are called boolean contexts:
- the condition of an
ifstatement - the
!(NOT) operator - the
&&and||operators - the condition of a
whileloop and a ternary? :
if ("hello") {
console.log("This runs, because non-empty text is truthy");
}
if (0) {
console.log("This never runs");
}
Every value is either truthy (becomes true) or falsy (becomes false).
The falsy values (memorise these 8)
Boolean(false); // false
Boolean(0); // false
Boolean(-0); // false
Boolean(0n); // false (BigInt zero)
Boolean(""); // false (empty string)
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
Everything else is truthy. That includes some values that look “empty” or “false”:
Boolean("0"); // true (text with a character in it)
Boolean("false"); // true (it is just text)
Boolean(" "); // true (a space is a character)
Boolean([]); // true (empty array)
Boolean({}); // true (empty object)
Boolean(-1); // true
The double NOT trick !!
! converts to boolean and flips it. Two of them flip it back, leaving you with the boolean version of the value:
console.log(!!"hello"); // true
console.log(!!0); // false
console.log(!!"0"); // true
Boolean(value) does the same thing, and many developers find it easier to read.
Truthy is not the same as == true
A truthy value is not always equal to true:
const items = [];
if (items) {
console.log("An empty array is truthy"); // runs
}
console.log(items == false); // true (very confusing!)
The array is truthy in an if, but loosely equal to false in ==, because == follows a different set of rules (it converts to numbers). That is another reason to avoid ==.
Logical operators return real values
&& and || do not always return true or false. They return one of the original values, after testing it as a boolean:
console.log("" || "Guest"); // "Guest" (first truthy value, or the last one)
console.log("Aman" || "Guest"); // "Aman"
console.log(0 || 10); // 10
console.log("a" && "b"); // "b" (both truthy: gives the last one)
console.log(null && "b"); // null (stops at the first falsy one)
Real-life use cases
Use case 1: reading form inputs
The value of an input box is always a string, even if the user types a number. This is the number one place where coercion causes bugs.
<input id="num1" type="text" value="5">
<input id="num2" type="text" value="3">
<button id="add">Add</button>
<p id="result"></p>
const num1 = document.getElementById("num1");
const num2 = document.getElementById("num2");
const result = document.getElementById("result");
document.getElementById("add").addEventListener("click", function () {
// Wrong: "5" + "3" joins the text
console.log(num1.value + num2.value); // "53"
// Right: convert first
console.log(Number(num1.value) + Number(num2.value)); // 8
result.textContent = Number(num1.value) + Number(num2.value);
});
Using - or * would have worked by accident, but + does not. Always convert input values explicitly.
Use case 2: checking whether the user typed something
Truthy and falsy make “is there any text?” checks short:
const userName = "";
if (userName) {
console.log("Hello, " + userName);
} else {
console.log("Please enter your name");
}
An empty string is falsy, so the else branch runs. One catch: a name of only spaces (" ") is truthy. Clean it with .trim() first (strings lesson).
Use case 3: default values
const nickname = "";
const displayName = nickname || "Guest";
console.log(displayName); // "Guest"
Be careful when 0 is a valid value:
const quantity = 0;
console.log(quantity || 1); // 1 (wrong! the user chose 0)
console.log(quantity ?? 1); // 0 (right)
You will see ?? in detail in the optional-chaining lesson.
Use case 4: removing falsy values from a list
Boolean can be passed as a filter. This is a handy one-liner that you will understand fully in the array lessons:
const data = [0, 1, "", "hello", null, undefined, NaN, false, 2];
const cleaned = data.filter(Boolean);
console.log(cleaned); // [1, "hello", 2]
A fun example: “banana”
console.log("ba" + +"a" + "a"); // "baNaNa"
Step by step: +"a" converts the text "a" to a number, which is NaN. Then "ba" + NaN joins as text: "baNaN", and finally "a" is added. Funny, but also a good test of whether you understand coercion.
Best practices
- Convert on purpose. Use
Number(),String()andBoolean()when you want a different type, so a reader can see your intention. - Use
===and!==. - Be careful with
+whenever one of the values might be a string, especially values from inputs,prompt()or URLs. - Learn the 8 falsy values. If you know them, truthy and falsy checks are never a surprise.
- Do not use clever tricks just to save a few characters. Code is read more often than written.
Common mistakes
- Adding input values without converting.
"5" + "3"is"53". - Assuming
"0"or"false"is falsy. Any non-empty string is truthy. - Assuming
[]and{}are falsy. They are truthy. Check.lengthfor arrays. - Comparing numbers as strings.
"10" < "9"istruebecause strings compare letter by letter. - Using
== nullor== 0without knowing the rules.null == 0isfalse, even thoughnullbecomes0in maths. - Printing an object with
+."User: " + usergives[object Object]. UseJSON.stringify(user)or log the object itself. - Forgetting that
||treats0and""as missing values.
Practice
-
Predict the output of each line, then check in the editor:
console.log("3" + 4 + 5); console.log(3 + 4 + "5"); console.log("10" - "4"); console.log("5" * "5"); console.log(true + 1); console.log(null + 5); console.log(undefined + 5); -
Click Practice in Editor and run the Add button. Fix the code so that 5 and 3 give
8, not53. -
Which of these are truthy? Check each with
Boolean(...):"0",0,[],""," ",null,-1,"false",NaN,{}. -
Explain, without running it, why
"10" > "9"isfalsebut"10" > 9istrue. -
Use
||to print"Anonymous"when a variableusernameis an empty string, and print the real name when it is filled in. -
Use
.filter(Boolean)to clean this array:["Riya", "", "Karan", null, "Neha", undefined].
Recap
- Type coercion is JavaScript converting types automatically. Conversion you do yourself is explicit, and the automatic one is implicit.
- Values are coerced into a string, a number or a boolean.
+joins text if either side is a string. All other math operators (- * / % **) convert to numbers.""andnullbecome0,undefinedbecomesNaN, andtruebecomes1.- In comparisons, a mix of a number and a string compares as numbers, but two strings compare as text (
"10" > "9"isfalse). ==coerces and gives strange results.===does not. Use===.- There are 8 falsy values:
false,0,-0,0n,"",null,undefined,NaN. Everything else is truthy, even"0",[]and{}. - Input values are always strings, so convert them before maths.
- Next you will work with strings: indexing, methods and template literals.