Nullish Coalescing
Use ?? for null/undefined defaults without treating 0 or empty string as missing — vs ||, ??= and optional chaining.
- javascript
- nullish-coalescing
|| as a default operator is a classic footgun: it treats all falsy values as missing. ?? (nullish coalescing) only falls through for null and undefined.
const count = input || 10;
// input = 0 → 10 (wrong if 0 is valid)
const count2 = input ?? 10;
// input = 0 → 0
// input = null → 10
Semantics
a ?? b
// If a is null or undefined, result is b; otherwise a.
// b is not evaluated if a is non-nullish (short-circuit).
| Left | left || right |
left ?? right |
|---|---|---|
0 |
right | 0 |
'' |
right | '' |
false |
right | false |
null |
right | right |
undefined |
right | right |
'hi' |
'hi' |
'hi' |
With optional chaining
const theme = user?.settings?.theme ?? 'system';
// missing chain → undefined → 'system'
?. stops on nullish and yields undefined; ?? supplies the default. They pair constantly in real UI code.
Assignment: ??=
options.timeout ??= 5000;
// assign only if options.timeout is null or undefined
// does not overwrite 0
Same family: ||=, &&= with falsy/truthy rules instead of nullish.
Precedence gotcha
// SyntaxError without parens in some mixes with && / ||
// const x = a ?? b || c; // illegal in JS
const x = (a ?? b) || c;
const y = a ?? (b || c);
The language forbids mixing ?? with &&/|| without parentheses so you state intent.
When || is still right
You want empty string / 0 / false to mean “use default”:
const label = (title || 'Untitled').trim();
// blank title → Untitled
That’s a product choice, not a language mistake — just don’t use || for numeric configs.
Defaults vs function parameters
function connect({ host = 'localhost', port = 8080 } = {}) {
// param defaults also only replace undefined, not null
}
connect({ port: null }); // port is null inside — not 8080
// if null should default:
function connect2({ port } = {}) {
const p = port ?? 8080;
}
Interview answer (out loud)
“
??returns the right side only when the left is null or undefined, so valid falsy values like 0 and empty string are kept.||collapses all falsy. Combine with?.for nested access, use??=for assign-if-nullish, and parenthesize if you mix with&&or||.”
Chaining nullish
const v = a ?? b ?? c ?? 'fallback';
Left-associative: first non-nullish wins. Combine with optional chaining carefully:
const port = opts?.server?.port ?? 8080;
React props defaults
function Avatar({ size, url }) {
const px = size ?? 40;
// vs default params: function Avatar({ size = 40 })
// default params also only replace undefined, not null
}
If a parent passes size={null} meaning “use default,” you need ?? in the body, not only default params.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.