JavaScript · Beginner
JavaScript truthy and falsy values without the guesswork
A complete, usable model of JavaScript boolean coercion—including the empty array trap, nullish values, NaN, and when to compare explicitly.
JavaScript conditions accept any value, not just a boolean. Before choosing a branch, the language converts that value to true or false. “Truthy” and “falsy” describe the result of that conversion; they are not extra data types.
if ('hello') {
console.log('runs');
}
if (0) {
console.log('does not run');
}
You can see the conversion directly with Boolean(value).
The complete falsy list
There are only eight falsy values in modern JavaScript:
| Falsy value | Meaning or common source |
|---|---|
false |
The boolean false |
0 |
Numeric zero |
-0 |
Signed numeric zero |
0n |
BigInt zero |
'', "", or `` |
An empty string |
null |
An intentional absence |
undefined |
A missing or unassigned value |
NaN |
An invalid numeric result |
Every other value is truthy. Memorising the short falsy list is more reliable than trying to remember thousands of truthy possibilities.
const values = [false, 0, -0, 0n, '', null, undefined, NaN];
values.map(Boolean); // [false, false, false, false, false, false, false, false]
document.all is a historical browser oddity with falsy behaviour, but it should not be used in application logic.
Empty arrays and objects are truthy
The most common surprise is that an empty container is still an object, and objects are truthy:
Boolean([]); // true
Boolean({}); // true
if ([]) {
console.log('this runs');
}
Test what you mean instead of testing the container itself:
const results = [];
if (results.length > 0) {
showResults(results);
} else {
showEmptyState();
}
For an object, you might test for a required property or inspect Object.keys(object).length, depending on the business rule.
The zero and empty-string problem
A truthiness check often accidentally treats a valid value as missing:
function showBalance(balance) {
if (!balance) {
return 'Balance unavailable';
}
return `$${balance}`;
}
showBalance(0); // "Balance unavailable"
Zero is a perfectly valid balance. If only null and undefined mean “missing,” use a nullish test:
function showBalance(balance) {
if (balance == null) {
return 'Balance unavailable';
}
return `$${balance}`;
}
The deliberate == null is one of the few useful loose comparisons: it matches both null and undefined, but not 0, false, or ''. If your codebase bans loose equality, write balance === null || balance === undefined.
|| and ?? answer different questions
The OR operator returns its right operand when the left operand is falsy. Nullish coalescing does so only for null or undefined:
const savedVolume = 0;
const withOr = savedVolume || 50; // 50
const withNullish = savedVolume ?? 50; // 0
Use || when every falsy value should trigger the fallback. Use ?? when zero, false, and an empty string are valid data.
const displayName = user.nickname || 'Anonymous';
const retryCount = settings.retries ?? 3;
The first line treats an empty nickname as absent. The second preserves zero retries.
NaN needs its own test
NaN is falsy, but a broad falsy check loses the distinction between an invalid number and zero:
const quantity = Number('three');
if (Number.isNaN(quantity)) {
console.log('Quantity must be numeric');
}
Prefer Number.isNaN() to the global isNaN(). The global function coerces its argument first and can label surprising inputs as numeric or non-numeric.
Boolean wrappers are objects
Avoid constructing booleans with new Boolean():
const misleading = new Boolean(false);
Boolean(misleading); // true
if (misleading) console.log('runs');
The wrapper contains false, but the wrapper itself is an object, so it is truthy. Use the primitive false or Boolean(value) without new.
When truthiness improves code
Truthiness reads well when the application meaning aligns with boolean coercion:
if (errorMessage) {
showError(errorMessage);
}
If an empty error message genuinely means there is nothing to show, the concise condition is accurate.
Use an explicit comparison when distinct falsy values have different meanings:
if (responseCode === 0) {
retryConnection();
} else if (responseCode === null) {
waitForResponse();
}
The goal is not maximum explicitness. It is preserving the distinctions your program cares about.
See the complete language reference on MDN’s falsy value page.
Coercion examples and the comparison table were executed with Node.js 22.14.0 and checked against MDN's falsy-value reference.