← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / Optional Chaining & Nullish Operators

Optional Chaining & Nullish Operators

Safe property access with ?., nullish coalescing with ??, nullish assignment ??=, and logical assignment — eliminating null checks from modern JavaScript.

What it is, in plain English: Optional chaining (?.) lets you read a property deep in an object chain without checking if each level exists — if any part is null or undefined, the expression short-circuits and returns undefined instead of throwing. The nullish coalescing operator (??) returns the right side only when the left side is null or undefined — unlike || which also triggers on 0, '', and false.
Where this bites you

?. doesn't make a missing value disappear; it turns it into undefined. Anything you compare with undefined afterwards is usually false. So cart?.items?.length === 0 is true for an empty array but false when there's no array at all, and the code skips the "empty" branch for exactly the user who has nothing yet.

function render(cart) {
  if (cart?.items?.length === 0) {
    return "Your cart is empty";
  }
  return `You have ${cart?.items?.length} item(s)`;
}

console.log(render({ items: [] }));   // a cart with no items
console.log(render({}));              // a new user: no items array yet
// Your cart is empty
// You have undefined item(s)

Safer code with fewer null checks

Two of the most common bugs in JavaScript involve null and undefined: trying to access a property on something that doesn't exist, and using || for defaults when 0 or "" should be valid values. Three operators fix both problems cleanly.

?. — Optional chaining

Read a deeply nested property without crashing if something in the chain is missing.

const order = {
  id: 1,
  customer: {
    name: "Alice",
    address: {
      city: "Mumbai"
    }
  }
};

// Without ?.  — you need a chain of checks:
const city1 = order && order.customer && order.customer.address
              && order.customer.address.city;

// With ?. — one clean expression:
const city2 = order?.customer?.address?.city;
console.log(city2);  // "Mumbai"

// If any step is missing — returns undefined, no crash:
console.log(order?.billing?.address?.city);  // undefined
// Without ?.: TypeError: Cannot read properties of undefined
// ?. on method calls:
const user = { getName: () => "Bob" };

console.log(user.getName?.());      // "Bob"
console.log(user.getEmail?.());     // undefined (method doesn't exist — no crash)

// ?. on array access:
const first = null;
console.log(first?.[0]);   // undefined (not: TypeError: Cannot read properties of null)

?? — Nullish coalescing (the better default operator)

Use ?? instead of || when 0, "", or false are valid values that should NOT trigger the default.

// The || problem:
function setVolume(vol) {
  const volume = vol || 50;  // BUG: volume=0 becomes 50!
  console.log("Volume:", volume);
}
setVolume(0);   // Volume: 50  ← wrong!
setVolume(80);  // Volume: 80  ← correct

// The ?? fix:
function setVolumeFix(vol) {
  const volume = vol ?? 50;  // Only defaults when vol is null/undefined
  console.log("Volume:", volume);
}
setVolumeFix(0);      // Volume: 0   ← correct
setVolumeFix(80);     // Volume: 80  ← correct
setVolumeFix(null);   // Volume: 50  ← correct (null → use default)
setVolumeFix(undefined); // Volume: 50 ← correct

Combining ?. and ??

const config = {
  server: {
    port: 0,           // 0 is a valid port
    timeout: null,     // null means "not set"
  }
};

const port    = config?.server?.port    ?? 3000;   // 0 (not 3000 — 0 is valid)
const timeout = config?.server?.timeout ?? 5000;   // 5000 (null → default)
const host    = config?.server?.host    ?? "localhost"; // "localhost" (undefined → default)

console.log(port, timeout, host);  // 0  5000  "localhost"

The config example above shows the habit that prevents this: follow ?. with ?? so a missing value becomes a real default before you compare it or print it. Work out the value once, then use it:

function render(cart) {
  const count = cart?.items?.length ?? 0;   // missing counts as zero
  if (count === 0) {
    return "Your cart is empty";
  }
  return `You have ${count} item(s)`;
}

console.log(render({ items: [] }));
console.log(render({}));
// Your cart is empty
// Your cart is empty
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.