How do optional chaining (?.) and nullish coalescing (??) work?

?. safely accesses nested properties without throwing if an intermediate value is null or undefined. ?? provides a fallback only for null/undefined.

JavaScript

Overview

Optional chaining (`?.`) and nullish coalescing (`??`) were introduced in ES2020 to eliminate verbose null checks when working with deeply nested data. Together they are the modern replacement for long `&&` chains and `||` fallback patterns.

Key Points

  • •`obj?.prop` returns `undefined` instead of throwing if `obj` is `null` or `undefined`.
  • •`arr?.[index]` and `fn?.()` - optional chaining also works on bracket access and function calls.
  • •`a ?? b` returns `b` only when `a` is `null` or `undefined` (unlike `||` which also catches `0`, `""`, `false`).
  • •They compose: `user?.address?.city ?? "Unknown"` safely navigates and falls back.
  • •Short-circuits: if the left side is `null`/`undefined`, the right side is not evaluated.

Examples

Optional chaining for nested object access

javascript
const user = {
  profile: {
    address: { city: 'Austin' }
  }
};

// Without optional chaining (verbose)
const city = user && user.profile && user.profile.address && user.profile.address.city;

// With optional chaining
const city = user?.profile?.address?.city;   // 'Austin'

// If address is missing:
const zip = user?.profile?.address?.zip;     // undefined (no throw)

Optional method calls

javascript
// Only call if the method exists
const result = obj.transform?.();

// Works with array access too
const first = arr?.[0];

// Call a callback only if provided
function process(data, callback) {
  const processed = transform(data);
  callback?.(processed); // no-op if callback is undefined
}

Nullish coalescing for fallback values

javascript
const config = { timeout: 0, retries: null };

// || is wrong here: 0 is falsy, falls back to 3000 incorrectly
const timeout = config.timeout || 3000;  // 3000 (BUG!)

// ?? only falls back for null/undefined
const timeout = config.timeout ?? 3000;  // 0 (correct)
const retries = config.retries ?? 3;     // 3 (retries was null)

Combining ?. and ?? for safe navigation with fallback

javascript
const displayName = user?.profile?.displayName ?? user?.name ?? 'Anonymous';

const streetLine = order?.shippingAddress?.street ?? 'No address on file';

Pro Tip

Do not over-use `?.` - if a value should always exist, optional chaining hides bugs by silently returning `undefined`. Only use it where `null`/`undefined` is genuinely expected (optional fields, API responses that might omit data).