Articles

JavaScript Optional Chaining and Nullish Coalescing

Optional chaining (?.) short-circuits on null or undefined instead of throwing; nullish coalescing (??) supplies a default only for null or undefined.

Takina Takina · · 4 min read
A code editor showing componentized source files

Optional chaining (?.) and nullish coalescing (??) are two JavaScript operators that make working with values that might be null or undefined far less painful, without the sprawling chains of && checks or verbose if statements developers used to write by hand. Optional chaining stops a property or method access short instead of throwing when something along the chain doesn’t exist; nullish coalescing supplies a fallback value, but only when the left side is actually null or undefined — not for other falsy values like 0 or "".

The problem they solve

Before these operators existed, safely reading a deeply nested property meant writing something like this:

const city = user && user.address && user.address.city;

Every additional level of nesting added another && check, and the pattern got harder to read exactly when the data got more complex — API responses with optional fields, config objects where entire sections might be missing, and so on. It also relied on treating any falsy value as “not there,” which is often not what you actually want.

Optional chaining

Optional chaining replaces that whole chain with a single operator:

const city = user?.address?.city;

If user is null or undefined, the expression short-circuits and evaluates to undefined immediately — it never tries to read .address off a nonexistent value, and never throws a TypeError. If user exists but address doesn’t, the same thing happens at that step. Only if every link in the chain exists does the final value come through.

The operator isn’t limited to property access. It works on method calls, guarding against a function that might not exist:

user.onLogin?.();

And on array/bracket access, useful when a key is dynamic or an array might be empty:

const first = items?.[0];

Crucially, optional chaining only guards against null and undefined. If user.address is an empty string or 0, the chain continues normally — those are valid values, not missing ones. This is a deliberate design choice that distinguishes “this doesn’t exist” from “this exists and happens to be falsy,” a distinction that plain && chaining couldn’t make.

Nullish coalescing

Nullish coalescing addresses a different, related problem: supplying a default value. Before it existed, || was the usual tool:

const timeout = config.timeout || 5000;

This looks reasonable until config.timeout is legitimately 0 — meaning “no timeout” — and the || operator overrides it anyway, because 0 is falsy. The same bug shows up with an empty string meant to clear a field, or false meant to explicitly disable something. || can’t tell the difference between “this value is absent” and “this value is falsy but intentional.”

?? fixes exactly that:

const timeout = config.timeout ?? 5000;

Now 5000 is only used if config.timeout is null or undefined. A config.timeout of 0 is respected as a real value. This is the whole point of the operator: it checks for nullishness, not falsiness.

Combining them

The two operators are frequently used together, since optional chaining naturally produces undefined when a chain breaks, and nullish coalescing is the natural way to give that result a fallback:

const city = user?.address?.city ?? "Unknown";
const port = config.server?.port ?? 8080;

This reads cleanly as “get this nested value if it exists, otherwise use a default” — the exact pattern that used to take several lines and several bugs to get right.

?? vs ||: know the difference

| | || (logical OR) | ?? (nullish coalescing) | |---|---|---| | Triggers default on | Any falsy value | Only null or undefined | | 0 on the left | Replaced by default | Kept as-is | | "" on the left | Replaced by default | Kept as-is | | false on the left | Replaced by default | Kept as-is | | Good for | “Give me any truthy value” | “Give me a value that was actually set” |

A related pitfall: ?? cannot be mixed directly with && or || in the same expression without parentheses — a || b ?? c is a syntax error by design, since the two operators have easily confused precedence and JavaScript forces you to be explicit about grouping.

Where these show up in real code

Both operators are especially common when working with API responses, where fields are often optional and their absence is meaningful. They also pair naturally with TypeScript’s optional properties (address?: Address) since the type system and the runtime check line up conceptually — a property marked optional in a type is exactly the kind of property worth accessing with ?.. They’re just as useful reading from configuration objects, environment-derived settings, or state coming out of a closure that might not have initialized every field yet.

Neither operator changes how async/await or promises behave — they operate purely on values already in hand — but they’re frequently used right after an await to safely dig into a response body without a chain of manual checks first.

The takeaway

Optional chaining (?.) stops property, method, or index access short the moment it hits null or undefined, instead of throwing. Nullish coalescing (??) supplies a fallback, but only for those same two values, leaving legitimate falsy values like 0, "", and false untouched. Together they replace a class of defensive && chains and || defaults that used to be both verbose and subtly buggy, and they’re worth reaching for by default anywhere a value’s presence isn’t guaranteed.

Takina Takina · · 5 min read

Promise.all() vs allSettled() vs race() Compared

Promise.all() fails fast, allSettled() waits for every result, and race() returns whichever promise finishes first — how to choose correctly.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

JavaScript Spread vs Rest Operators, Explained

The spread operator (...) expands an iterable into individual elements; the rest operator collects elements back into an array. Same syntax, opposite jobs.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

ResizeObserver API Explained: Watching Element Size

The ResizeObserver API lets JavaScript watch an element's box size and react without polling or resize-event hacks. How it works and when to use it.

#Web Development #JavaScript #Frontend