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

🌐 Real-World Analogy: Coercion is like an over-eager travel agent who auto-converts currency:

🧠 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

Lesson Complete — Type Coercion!

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

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.