Type Coercion (== vs ===, truthy & falsy)
Reviewed & published by Brayan K
JavaScript has a habit of quietly converting values between types. It's why "5" + 1 is "51" but "5" - 1 is 4. These "WAT" moments confuse beginners, but once you understand the rules they become completely predictable.
Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
This lesson demystifies coercion, the falsy values, and the single habit that prevents most equality bugs: always use ===.
What You'll Learn in This Lesson
- Implicit vs explicit coercion
- The + operator's split personality
- All eight falsy values (and why the rest are truthy)
- == vs === and the messy loose-equality rules
- Boolean conversion and the !! trick
- How to convert types safely on purpose
🌐 Real-World Analogy: Coercion is like an over-eager travel agent who auto-converts currency:
- • You hand over dollars; they silently swap them for the local currency
- • Convenient — until the "exchange rate" surprises you at checkout
- • == is letting the agent convert for you; === is insisting on exact change
- • Doing conversions yourself (explicit) means no nasty surprises
🧠 Implicit vs Explicit Coercion
Implicit coercion happens automatically inside operations. Explicit coercion is when you convert on purpose. The same conversion, two ways:
➕ The + Operator's Split Personality
+ means addition and string concatenation. If either operand is a string, JavaScript concatenates; otherwise it adds. Every other math operator (- * / %) always coerces to numbers.
(The blank line is the empty string from [] + [].)
🌓 Truthy & Falsy — Memorize the Falsy Eight
Any value used in a boolean context (an if, &&, !) is coerced to true or false. There are exactly eight falsy values — everything else is truthy.
⚖️ == vs === — The Big One
=== (strict) compares value and type with no conversion. == (loose) coerces first, which leads to genuinely bizarre results. The lesson: use ===.
Look at the contradiction: 0 == "" and 0 == "0" are both true, yet "" == "0" is false. Loose equality is not transitive — another reason to avoid it.
🔄 Converting Types on Purpose
When you control the conversion, your code is clear and bug-resistant. Here are the idiomatic patterns:
// The classic + surprises
console.log("3" + 4); // "34"
console.log("3" - 4); // -1
console.log(3 + 4 + "5"); // "75" (left to right: 7, then "75")
// Truthy / falsy in action
const inputs = [0, "0", "", "hi", null, [], NaN];
for (const v of inputs) {
console.log(JSON.stringify(v), "->", Boolean(v));
}
// == vs === side by side
console.log("Loose 0 == '':", 0 == "");
console.log("Strict 0 === '':", 0 === "");
// Safe explicit conversion
function toCount(input) {
const n = Number(input);
return Number.isNaN(n) ? 0 : n;
}
console.log(toCount("12")); // 12
console.log(toCount("oops")); // 0
// Try it: predict each line's output BEFORE running it!// 🎯 YOUR TURN — replace each ___ using the hint beside it.
// Read each line and predict the answer BEFORE you run it.
// 1) + with a string on either side means JOIN, not add.
console.log("1 + '2' =", 1 ___ "2"); // 👉 replace ___ with +
// 2) Every other arithmetic operator converts to number instead.
console.log("'3' * '4' =", "3" ___ "4"); // 👉 replace ___ with *
// 3) == converts before comparing. This is the loose one.
console.log("1 == '1' =", 1 ___ "1"); // 👉 replace ___ with ==
// 4) === compares type as well, so it says false here.
console.log("1 === '1' =", 1 ___ "1"); // 👉 replace ___ with ===
console.log("null == undefined =", null == undefined);
console.log("null === undefined =", null === undefined);
console.log("[] + {} =", [] + {});
// 5) A non-empty string is truthy — even the string "0".
console.log("Boolean('0') =", ___("0")); // 👉 replace ___ with Boolean
console.log("Number('') =", Number(""));
console.log("'5' - 3 =", "5" - 3);
console.log("'5' + 3 =", "5" + 3);
console.log("[1,2] + [3] =", [1, 2] + [3]);
// ✅ Expected output:
// 1 + '2' = 12
// '3' * '4' = 12
// 1 == '1' = true
// 1 === '1' = false
// null == undefined = true
// null === undefined = false
// [] + {} = [object Object]
// Boolean('0') = true
// Number('') = 0
// '5' - 3 = 2
// '5' + 3 = 53
// [1,2] + [3] = 1,23🧩 Reorder Challenge
These lines should safely turn a form value into a number, defaulting to 0 for invalid input, and report whether it's positive. They are scrambled — reorder them.
B → D → A → C
Why: the raw text (B) must exist before conversion (D). Number trims the surrounding spaces and yields 42. The guard (A) replaces NaN with 0 so bad input can't poison later math. Only then can the result be reported (C). Note the final + here is string concatenation because the left side starts with a number followed by a string literal.
🧠 Quick Recall
123 — evaluated left to right: 1 + "2" concatenates to "12" (because one side is a string), then "12" + 3 concatenates to "123".
2. What is the result?
Only A prints. The string "0" is non-empty, so it's truthy. The number 0 is one of the eight falsy values, so the second if is skipped.
3. What does this log?
true false — loose == has a special rule making null and undefined equal to each other. Strict === checks type too, and they are different types, so it's false.
⚠️ Common Mistakes to Avoid
- 1️⃣ Using == for comparisons. Its coercion rules are inconsistent — default to ===.
- 2️⃣ Assuming "0" is falsy. Only the empty string is falsy; any non-empty string is truthy.
- 3️⃣ Adding user input directly. Form values are strings, so price + tax may concatenate — convert with Number() first.
- 4️⃣ Comparing to NaN. x == NaN is always false; use Number.isNaN(x).
Lesson Complete — Type Coercion!
- ✅ Coercion can be implicit (automatic) or explicit (on purpose)
- ✅ + concatenates if either side is a string; other math operators force numbers
- ✅ Eight falsy values exist; everything else — including "0" and [] — is truthy
- ✅ Always use === to avoid the chaos of loose equality
Practice quiz
What does '5' + 1 evaluate to?
- 6
- '6'
- '51'
- 51
Answer: '51'. The + operator concatenates when either side is a string, producing the string '51'.
What does '5' - 1 evaluate to?
- 4
- '4'
- '51'
- NaN
Answer: 4. Unlike +, the - operator always coerces operands to numbers, so '5' - 1 is 4.
What does 1 + '2' + 3 evaluate to?
- 6
- '15'
- 123
- '123'
Answer: '123'. Left to right: 1 + '2' is '12', then '12' + 3 is '123'.
How many falsy values are there in JavaScript?
- Five
- Eight
- Six
- Ten
Answer: Eight. There are exactly eight: false, 0, -0, 0n, '', null, undefined, and NaN.
Is the string '0' truthy or falsy?
- Truthy
- Falsy
- It throws
- Depends on context
Answer: Truthy. Only the empty string is falsy; any non-empty string, including '0', is truthy.
What does 0 == '' evaluate to?
- false
- An error
- true
- undefined
Answer: true. Loose equality coerces both sides to 0, so 0 == '' is true.
What does null == undefined evaluate to?
- false
- true
- An error
- NaN
Answer: true. A special loose-equality rule makes null and undefined equal to each other.
What does null === undefined evaluate to?
- true
- An error
- undefined
- false
Answer: false. Strict equality checks type too, and null and undefined are different types, so it is false.
What does NaN == NaN evaluate to?
- true
- false
- NaN
- An error
Answer: false. NaN is never equal to anything, including itself; use Number.isNaN to test for it.
What is the recommended habit to avoid loose-equality bugs?
- Always use ==
- Avoid comparisons entirely
- Always use ===
- Use Boolean() before comparing
Answer: Always use ===. The lesson stresses always using === to avoid the inconsistent coercion rules of ==.
Continue this course
- Previous: Conditionals & Operators
- Next: Hoisting — How var and function declarations are 'lifted' — and why let/const aren't
- Quick reference: JavaScript cheat sheet › Variables & Types
Frequently asked questions
What exactly is type coercion?
Type coercion is JavaScript automatically converting a value from one type to another. Implicit coercion happens behind your back during operations like '5' + 1 or if (value). Explicit coercion is when you do it on purpose with Number(x), String(x), or Boolean(x). The implicit kind is the source of most surprises.
Why should I always use === instead of ==?
== compares after coercing the two sides to a common type, which produces baffling results like 0 == '' being true and null == undefined being true. === compares value AND type with no conversion, so it only returns true when both genuinely match. Using === eliminates a whole category of bugs; reach for == almost never.
What are the falsy values in JavaScript?
There are exactly eight: false, 0, -0, 0n (BigInt zero), '' (empty string), null, undefined, and NaN. Everything else is truthy — including '0', 'false', [], {}, and any function. Memorizing the short falsy list is easier than memorizing the infinite truthy one.
Why is an empty array truthy but equals false with ==?
In a boolean context, [] is an object reference, and all objects are truthy — so if ([]) runs. But [] == false coerces both sides: false becomes 0, and [] becomes '' then 0, so 0 == 0 is true. This contradiction is a perfect example of why you should avoid == and never rely on loose equality with arrays or objects.
How do I convert types explicitly and safely?
Use Number(x) or parseInt/parseFloat for numbers, String(x) or template literals for text, and Boolean(x) or the !! double-negation for booleans. Always validate numeric conversions with Number.isNaN, since Number('abc') yields NaN and Number('') sneakily yields 0.