Optional chaining and ??: safe access without hiding real values
Read nested data that may be missing with ?., pick fallbacks with ?? instead of ||, and use ??=, ||= and &&=. With the 0 and empty-string traps.
Comes up in: JavaScript, Recruiter screen
Learn#
Priya’s music app crashes for new users: user.profile.avatar.url throws because new users have no profile. Old code guarded it with user && user.profile && user.profile.avatar && ….
Optional chaining, ?., stops and returns undefined when the value before it is null or undefined. It short-circuits: nothing to the right runs.
const user = { name: "Priya" };
let calls = 0;
console.log(user.profile?.avatar.url);
console.log(user.profile?.avatar[calls++], calls);
console.log(user.save?.());Output (verified)
undefined undefined 0 undefined
Her second bug: a muted user’s volume of 0 becomes 50. || falls back for every falsy value: 0, "", false, NaN. Nullish coalescing, ??, falls back only for null or undefined.
State
- settings = { volume: 0 }
settings.volume is 0: the user muted the sound on purpose.
Read the steps as text
- settings.volume is 0: the user muted the sound on purpose.
- settings.volume || 50 sees a falsy 0 and picks 50. The user's choice is lost.
- settings.volume ?? 50 falls back only for null or undefined. 0 is kept.
Predict#
Predict first
What does console.log("" ?? "x", 0 || "y", null ?? 0) print?
"" ?? "x" keeps the empty string, because "" is not null or undefined. 0 || "y" gives “y” because 0 is falsy. null ?? 0 gives 0. The tempting answer treats ?? like ||.
Interview angle#
Say it like this — about 30 seconds
“Optional chaining returns undefined instead of throwing when the value before ?. is null or undefined, and it skips the rest of the expression. Nullish coalescing gives a fallback only for null or undefined, while || falls back for any falsy value, so it overwrites 0, empty strings and false. I use ?? for settings and counts. The assignment forms ??=, ||= and &&= assign only when that condition holds.”
Follow-up What is the difference between ?? and ||?
|| falls back for any falsy value (0, "", false, NaN, null, undefined). ?? falls back only for null and undefined.Follow-up Can you mix ?? with || without parentheses?
a || b ?? c is a SyntaxError; you must write (a || b) ?? c.Follow-up What does a.b ??= 5 do?
a.b is null or undefined. Otherwise it does nothing, not even run the setter.Practice#
Guided
Fix the four functions. Each one either crashes on missing data or throws away a real value.
Done when
citynever throws, even forundefinedor a user with no address.pageSize({ pageSize: 0 })returns 0.greetworks whether or notformatexists.ensureTagskeeps an existing empty array or empty string.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When does a?.b return undefined without reading b?
When a is null or undefined.
Which values make ?? use its fallback?
Only null and undefined.
What does this print?console.log(0 || 5, 0 ?? 5)
console.log(0 || 5, 0 ?? 5)5 0
What does x ||= y do?
Assigns y to x only if x is falsy.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Three forms: property a?.b, computed a?.[key], and call fn?.(). The call form checks that fn is not null or undefined; it still throws if fn is a non-function such as a number.
Check yourself: Which forms of optional chaining exist?
a?.b, a?.[key] and fn?.()
How it works underneathSenior rounds
Short-circuiting covers the whole chain after ?., so a?.b.c.d does not throw when a is null. But (a?.b).c does throw, because the parentheses end the chain Source: MDN: Optional chaining.
Check yourself: In a?.b.c.d, if a is null, is .c read?
No. The whole chain short-circuits to undefined.
Edge cases and trapsWhere people slip
Mixing ?? with || or && without parentheses is a SyntaxError, so the meaning is never ambiguous Source: MDN: Nullish coalescing operator. Optional chaining cannot be on the left of an assignment: a?.b = 1 is a SyntaxError.
Check yourself: Is a || b ?? c valid JavaScript?
No, it is a SyntaxError; add parentheses.
In productionStaff-level answers
cache[key] ??= compute() runs compute only when the entry is missing, opts.debug ||= false sets a falsy value, and user &&= normalize(user) changes it only if it exists.?. deeper in the code.Check yourself: Why prefer cache[key] ??= compute() over an if check?
It is shorter, and compute only runs when the entry is missing.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is the difference between
??and||?