Values, types, and the equality trap
typeof, truthy and falsy, and the one comparison operator to stop reaching for.
After this lesson you can
- Name JavaScript's primitive types and tell them from objects
- Predict whether a value is truthy or falsy without running it
- Explain why === is the default and when == still surprises people
JavaScript has seven primitive types: string, number, boolean,
undefined, null, symbol, and bigint. Everything else — arrays,
functions, dates, plain objects — is an object. There is no separate
integer type: 1 and 1.5 are both a number (bigint, written 10n,
is a separate type for integers of any size).
typeof "hi"; // "string"
typeof 42; // "number"
typeof undefined; // "undefined"
typeof null; // "object" — an old bug nobody can fix now
typeof []; // "object"
typeof (() => {}); // "function"
typeof null returning "object" is not a design choice; it is a bug
from JavaScript's first implementation that shipped before anyone could
fix it without breaking the web. Comparing value === null directly is
the reliable way to ask.
Truthy and falsy
Every value is truthy in a boolean context except a short, fixed list:
false, 0, -0, 0n (the bigint zero), "", null, undefined,
and NaN. Everything else — including "0", [], and {} — is
truthy.
if ("0") console.log("truthy"); // runs: a non-empty string
if ([]) console.log("truthy"); // runs: an object, always truthy
Try it
export function falsyOnly() { const values = [0, 1, "", "a", null, "0", [], {}, false, true]; return values.filter((v) => !v);}== against ===
== compares after converting both sides to a common type. ===
compares without converting anything. The conversion rules for == are
a genuine, memorised list of special cases:
0 == "0"; // true — the string converts to a number
0 == ""; // true — empty string converts to 0
0 == false; // true — false converts to 0
null == undefined; // true — the one pair == treats as equal to each other
null == 0; // false — == never converts null: it equals only null and undefined
"" == "0"; // false — neither side is a number here, plain string compare
Nobody has that table memorised reliably, which is why the convention is:
use === and !== everywhere, and reach for == only for the one
legitimate case — value == null, which is true for both null and
undefined and nothing else.
NaN is never equal to anything, including itself
NaN === NaN; // false
Number.isNaN(NaN); // true — the reliable check
isNaN(x) (the global, not Number.isNaN) first converts x to a
number, so isNaN("hello") is also true. Use , which
never converts its argument. Every comparison in this lesson, run:
export function run() { return { typeofNull: typeof null, typeofArray: typeof [], typeofArrow: typeof (() => {}), zeroEqualsStringZero: 0 == "0", zeroEqualsEmpty: 0 == "", zeroEqualsFalse: 0 == false, nullEqualsUndefined: null == undefined, nullEqualsZero: null == 0, emptyEqualsStringZero: "" == "0", nanEqualsNaN: NaN === NaN, globalIsNaN: isNaN("hello"), numberIsNaN: Number.isNaN("hello"), };}Common mistakes
if (value)to mean "was something supplied". It also rejects0and"", which are often valid input — a quantity of zero, an empty search box. Compare againstnullandundefinedinstead.typeof x === "object"to mean "x is an object". It is also true fornull, so the nextx.propthrows. Addx !== null.x === NaN. Alwaysfalse, even whenxisNaN. UseNumber.isNaN(x).- Comparing form input with
==.0 == ""istrue, so an empty field quietly equals zero.
What interviewers ask
- What does
typeof nullreturn, and why? — : a bug from the first implementation, kept so old pages keep working. Test with .
Try it yourself
2 visible tests · 2 hidden testsImplement firstTruthy(values) that returns the first truthy value in
the array, or null if every value is falsy. Do not convert the value —
return it exactly as it was given.
firstTruthy([0,"",null,5,"a"])firstTruthy([0,"",null,false])
Sign up to run the tests, check the hidden ones and save your progress. Sign up