Ch. 1 · JavaScript

JavaScript Optional Chaining and Nullish Coalescing

Short-circuit safely with optional chaining and choose ?? over || when 0, empty strings and false are valid values.

~2 min readbeginnerupdated Oct 5, 2026

?. stops evaluating as soon as it meets null or undefined, so a deep read does not throw. ?? supplies a fallback for exactly those two values, unlike ||, which also falls back on 0, '' and false. Choosing between ?? and || is the difference between preserving a valid falsy value and silently discarding it.

Before you start

You should be comfortable with truthiness, &&/||, and property access on nested objects. Every example runs in a modern Node.js version or browser.

Step-by-step walkthrough

Step 1: Guard each optional step

Write order?.customer?.address?.city so a missing customer yields undefined instead of throwing. Each ?. guards only the access immediately after it, so every step that may be absent needs its own operator.

Step 2: Call and index safely

handler?.() calls the function only when it is not nullish, and rows?.[0] guards an index access. Both avoid a TypeError on a missing middleware or empty list, and both short-circuit the whole chain.

Step 3: Pick the right fallback

Use ?? when a falsy value is meaningful: const retries = config.retries ?? 3 keeps 0. Use || only when you intend every falsy value to trigger the fallback. Because mixing ?? with || without parentheses is a syntax error, group the intent explicitly if you must combine them.

Worked scenario

Run this with Node.js. The nullish operator keeps 0, while the logical OR replaces it.

const config = { retries: 0, name: '' };
console.log(config.retries ?? 3); // 0
console.log(config.retries || 3); // 3
console.log(config.user?.address?.city); // undefined
JavaScript

Walk through the example

retries is 0, which is nullish-neither null nor undefined, so ?? keeps it while || treats 0 as falsy and returns 3. The chained access finds no user, so it short-circuits to undefined rather than throwing. Notice that ?? distinguishes “value is zero” from “value is missing”, which is exactly what a default should do.

Common mistake

Using || to set defaults for numeric settings, turning a deliberate 0 or a valid '' into the fallback. A second mistake is assuming ?. continues a chain past a defined null: actually null?.x returns undefined, but null?.x.y still short-circuits, so no error occurs. The guard is on propagation, not a value check.

Verify the behavior

Assert that 0, '', false and NaN are preserved by ?? and replaced by ||. Test obj?.method?.() with a missing method and confirm it returns undefined without calling anything. Confirm arr?.[0] returns undefined for an undefined array but still reads a real first element.

Interview exercise

A settings screen shows “off” for a user who set retries to 0. The code reads settings.retries || 3. What happened?

Answer and reasoning

The || operator treated 0 as falsy and replaced it with the default 3, so the user’s explicit value was discarded. Switch to settings.retries ?? 3, which overrides only when the value is null or undefined. This is the standard signal that ?? is the correct default operator whenever falsy-but-valid values exist.

Continue learning

See related defaulting behavior in nullish defaults and read the MDN optional chaining reference. Continue with the JavaScript MCQs.

More in JavaScript

read ✓JavaScript · mid

JavaScript Date and Timezone Pitfalls

Parse and format dates without timezone surprises: zero-based months, date-only versus date-time parsing, and why UTC storage avoids drift.

~3 min readread →
read ✓JavaScript · mid

JavaScript Number Precision and BigInt

Why 0.1 + 0.2 is not 0.3, where safe integer range ends, and when BigInt is the right tool for large identifiers and money.

~2 min readread →
esc