You may remember to use the || operator with caution to set defaults. We'll see that && comes with the same limitations. However, TypeScript and eslint can make their usage a lot safer for us.
TL;DR
-
&&returns the first falsey value, or the second value if both are truthy -
||returns the first truthy value, or the second value if both are falsey - Both operators narrow types on each side, just like
if/else -
Danger:
0,'', andNaNare falsey - Prefer
??(nullish coalescing) or enablestrict-boolean-expressionsto avoid the 0/empty-string
&& as short-circuit for nullable access
Returns the first falsey value encountered, or the last value if all are truthy.
// Equivalent if/else
function arrayLength(strings: string[] | undefined): number | undefined {
if (strings === undefined) return undefined;
else return strings.length;
}
// Terse &&
function arrayLength(strings: string[] | undefined): number | undefined {
return strings && strings.length;
}
On the right side of &&, TypeScript knows strings is not undefined as it has been narrowed to string[].
|| as short-circuit for fallback values
Returns the first truthy value encountered, or the last value if all are falsey.
// Equivalent if/else
function numberOrOne(n: number | undefined): number {
if (n === undefined) return 1;
else return n;
}
// Terse ||
function numberOrOne(n: number | undefined): number {
return n || 1;
}
The 0 / empty-string bug
0 and '' are falsey, so || treats them as "missing" even when they are valid values.
function numberOrOne(n: number | undefined): number {
return n || 1;
}
numberOrOne(3); // 3
numberOrOne(0); // 1, but should be 0!
The same trap applies to && when a valid 0 or '' would short-circuit prematurely.
How to fix it
Option 1: Use ?? (nullish coalescing)
Instead of ||, ?? only fallbacks on null and undefined. It does not on 0 or ''.
function numberOrOne(n: number | undefined): number {
return n ?? 1;
}
numberOrOne(0); // 0
numberOrOne(undefined); // 1
Option 2: Use explicit comparison
return n !== undefined ? n : 1;
Option 3: Enable strict-boolean-expressions (recommended)
The
@typescript-eslint/strict-boolean-expressions
Show archive.org snapshot
rule forbids non-boolean types in boolean positions, catching the entire class of ||/&& bugs at lint time.
// eslint.config.mjs
export default tseslint.config({
rules: {
"@typescript-eslint/strict-boolean-expressions": "error"
}
});
The rule enforces explicit nullability checks on potentially unsafe types:
let num: number | undefined = 0;
if (num) { console.log('num is defined'); } // Unsafe because num could be 0
if (num != null) { console.log('num is defined'); } // This is safe
TypeScript catches swapped operators
Accidentally using || instead of && (or vice versa) usually produces a type error:
function arrayLength(strings: string[] | undefined): number | undefined {
return strings || strings.length; // type error: 'strings' is possibly 'undefined'
}
TypeScript narrows strings to undefined on the right side of || (because that side runs when strings is falsey). So accessing .length there is an error.
Ternary
?:operatorThe ternary operator
?:actually also allows us to narrow the type and does not suffer from the falsy bugs.