ESC

Type to search the knowledge base.

Optional Chaining

Safe property/call access with ?. — short-circuit rules, arrays, nullish defaults, and mistakes that hide real bugs.

beginner3 min read
  • javascript
  • optional-chaining

Deep property access used to need nested checks or && chains. Optional chaining (?.) stops evaluation when it hits null or undefined and yields undefined instead of throwing.

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

// after
const city = user?.address?.city;

Forms

obj?.prop
obj?.[expr]
fn?.(args)
const users = [{ name: 'Ada' }];
users?.[0]?.name;     // 'Ada'
users?.[5]?.name;     // undefined

api?.getUser?.(id);   // call only if getUser is non-nullish

If the left side is nullish, the rest of that chain segment is skipped. If it’s non-nullish but not a function, fn?.() still throws — ?. only guards nullish, not “wrong type.”

const x = { f: 1 };
x.f?.(); // TypeError: x.f is not a function

Short-circuit, not try/catch

let n = 0;
const v = null?.[++n];
n; // 0 — right side not evaluated

const w = obj?.compute() ?? 0; // compute not called if obj nullish

Pair with ??

const theme = settings?.theme ?? 'light';
const len = list?.length ?? 0;

?. → undefined on missing; ?? → default only for nullish (keeps 0).

Assignment is not allowed

// SyntaxError
// user?.name = 'x';

Optional chaining is for reads (and calls), not writes. Write after a guard:

if (user) user.name = 'x';

Overuse is a smell

// Hides broken contracts
const id = response?.data?.user?.id;
// If API always returns data.user, failing open to undefined delays the bug

Use ?. at real optional boundaries (partial data, optional widgets, progressive enhancement). At hard invariants, throw or validate (Zod, etc.).

delete and other operators

delete obj?.prop; // deletes if obj non-nullish; no-op-ish path if nullish (doesn’t throw)

Prefer explicit null checks for mutating operations — clarity over cleverness.

Interview answer (out loud)

“?. short-circuits when the value before it is null or undefined and returns undefined. It works for properties, computed keys, and calls. It doesn’t make bad types safe — calling a non-function still throws. I combine it with ?? for defaults and avoid sprinkling it so deeply that missing data never surfaces.”

Long chains and debugging

const tax = order?.customer?.address?.country?.taxRate;
// If undefined, which link failed? Hard to tell.

When debugging intermittent data bugs, temporary explicit checks or logging at each level beat a single mega-chain. In production code, validate API payloads at the boundary so UI code can use fewer ?..

Optional call on methods

const maybeLogger = cond ? console : null;
maybeLogger?.log('hi');

obj.onComplete?.(); // fine if missing method

Useful for optional callbacks in library options objects.

Bracket access and symbols

obj?.[key];
obj?.[sym]; // works with symbol keys

Dynamic keys from user input still need allowlists — optional chaining doesn’t make property access “safe” from prototype pollution if you assign with user keys elsewhere.

Further reading

Related guides