Optional Chaining
Introduction
Optional chaining (?.) safely reads a property, calls a method, or indexes a value only when the value to its left is not null or undefined. If the left side is nullish, the whole optional chain short-circuits to undefined.
It is not a replacement for validation; it is a concise way to express "continue only if this object exists".
Why This Matters
Real applications consume incomplete data: API responses, feature flags, optional callbacks, and configuration objects. Optional chaining prevents defensive code from turning into nested if statements while preserving the important distinction between missing data and falsy-but-valid data.
Theory
Forms of optional chaining
- Property access:
user?.profile. - Element access:
rows?.[0]. - Optional call:
onSuccess?.(result).
The operator checks only the value immediately to its left. In user?.profile.name, user is protected, but profile.name is not optional. Use user?.profile?.name if both levels may be missing.
Short-circuiting
When optional chaining short-circuits, later property lookups, index expressions, and call arguments in that chain are not evaluated. This matters for side effects such as counters or function calls inside arguments.
Result is undefined
The fallback result is always undefined, not null. Combine optional chaining with nullish coalescing when you need a default: user?.profile?.city ?? 'Unknown'.
Limits
Optional chaining cannot be used on the left side of assignment (user?.name = 'Ada' is invalid). It also does not catch errors thrown by an existing getter or method; it only avoids accessing through null or undefined.
Visual Diagrams
user?.profile?.city | +-- user is null or undefined? yes -> undefined | no v profile is null or undefined? yes -> undefined | no v read city
Each optional hop guards only the value immediately before it.
Code Examples
Property, element, and call forms
Optional chaining is available for the three access patterns you use most often.
Pairing with nullish coalescing
Optional chaining returns undefined; ?? provides a default only for nullish results.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1let calls = 0;2const user = null;3 4const result = user?.profile?.getCity(calls++);5 6console.log(result);7console.log(calls);Predict the output #2
1const user = {2 profile: {3 getName: function () {4 return 'Ada';5 }6 }7};8 9console.log(user?.profile?.getName?.());10console.log(user?.settings?.theme ?? 'light');Coding Exercises
Read a safe profile summary
EasyImplement profileSummary(user) so it returns { name, city, company }. Use optional chaining and nullish coalescing so missing values become 'Unknown', but an empty string is preserved.
Interview Questions
1What exactly does optional chaining guard against?
It guards against the value immediately to its left being null or undefined. If that value is nullish, the chain returns undefined. It does not guard against other falsy values, and it does not suppress errors thrown by existing getters or methods.
2Why might `user?.profile.name` still throw?
Only user is optional in that expression. If user exists but profile is undefined, then .name is attempted on undefined and throws. Use user?.profile?.name when both levels are optional.
Follow-ups
- How does optional call syntax work?
- What value does an optional chain produce when it short-circuits?
Quiz
1. What is the result of `null?.x`?
2. Which expression safely calls `onDone` only if it exists?
Summary
- Optional chaining guards property access, element access, and function calls against `null` and `undefined`.
- Each `?.` protects only the value immediately to its left.
- A short-circuited optional chain returns `undefined` and skips later side effects in the chain.
- Combine `?.` with `??` to provide defaults without replacing valid falsy values.
Cheat Sheet
Property: obj?.prop.
Element: arr?.[index].
Call: callback?.(value).
Nested: use ?. at every uncertain level: user?.profile?.city.
Default: user?.profile?.city ?? 'Unknown'.