Nullish Coalescing & Optional Chaining

JavaScript · Lesson 13 of 61 6 min read Updated

Real data is often incomplete. A user may not have filled in their city, an order may have no gift note, a setting may be missing from an old saved profile. If your code assumes every value is there, it prints undefined on the page or crashes with an error.

In this lesson you will learn three modern tools for missing values: the nullish coalescing operator ??, optional chaining ?., and the logical assignment operators ??=, ||= and &&=.

The examples use objects, which group named values together. order.discount means "the discount value inside order". You will study objects fully in Objects; for now, reading values with a dot is all you need.

Default values: the problem with ||

In Operators you saw that || returns its right side when the left side is falsy. Older code uses this for defaults: const volume = savedVolume || 50;. The trouble is that 0 and "" are falsy too, even when they are perfectly good values.

JavaScriptRun it yourself
const order = { discount: 0, giftNote: null };

console.log(order.discount || 10);
console.log(order.discount ?? 10);
console.log(order.giftNote ?? "No note");
console.log(order.deliverySlot ?? "Any time");
Output
10
0
No note
Any time

The order has a discount of 0, a real value. || threw it away and used 10 instead, which is a bug. The nullish coalescing operator ?? only uses the right side when the left side is null or undefined, which together are called nullish values. 0 is kept. giftNote is null and deliverySlot does not exist (undefined), so both fall back to the default.

Left sideleft || "default"left ?? "default"
null"default""default"
undefined"default""default"
0"default"0
"""default"""
false"default"false
"Pune""Pune""Pune"

Use ?? when you mean "if there is no value". Use || only when you really want to replace every falsy value, such as an empty name.

Optional chaining: ?.

Reading a property from something that is undefined or null throws an error. That happens a lot with nested data: one missing level breaks the whole expression.

JavaScriptRun it yourself
const user = {
  name: "Ananya",
  address: { city: "Kochi" },
};
const guest = { name: "Guest" };

console.log(user.address.city);
console.log(guest.address?.city);
console.log(guest.address?.city ?? "City not given");
Output
Kochi
undefined
City not given

guest has no address. Writing guest.address.city would throw "Cannot read properties of undefined". With optional chaining ?., JavaScript checks the value before the ?.: if it is null or undefined, the whole expression stops and gives undefined instead of an error. Combined with ??, you get a clean default in one line.

Optional chaining short-circuits: in a?.b.c.d, if a is nullish, JavaScript does not even look at .b, .c or .d.

Brackets and function calls

?. also works with square brackets and with function calls:

JavaScriptRun it yourself
const order = {
  items: ["Masala dosa", "Filter coffee"],
  track() {
    return "Out for delivery";
  },
};
const emptyOrder = {};

console.log(order.items?.[0]);
console.log(emptyOrder.items?.[0]);
console.log(order.track?.());
console.log(emptyOrder.track?.());
Output
Masala dosa
undefined
Out for delivery
undefined
  • items?.[0] reads the first item only if items exists.
  • track?.() calls the function only if it exists; otherwise the result is undefined.

Logical assignment: ??=, ||= and &&=

These operators combine a check with an assignment. They are handy for filling in defaults on an existing object.

JavaScriptRun it yourself
const profile = { name: "Rohan", city: "", visits: 0 };

profile.city ||= "Not set";       // "" is falsy, so it is replaced
profile.visits ??= 1;             // 0 is not nullish, so it stays
profile.language ??= "English";   // missing, so it is added
profile.name &&= profile.name.toUpperCase(); // runs only if name is truthy

console.log(profile.city, profile.visits, profile.language, profile.name);
Output
Not set 0 English ROHAN
OperatorAssigns only when the variable isSame as
a ??= bnull or undefineda ?? (a = b)
a ||= bfalsya || (a = b)
a &&= btruthya && (a = b)

Rules and good habits

You cannot mix ?? with || or && without brackets. JavaScript refuses to guess which you meant and throws a SyntaxError:

JavaScript
const a = null, b = 0, c = 5;

// const x = a || b ?? c;    // SyntaxError
const x = (a || b) ?? c;     // OK: brackets make the order clear
console.log(x);              // 0

Do not put ?. everywhere. If a value must always exist, such as the id of a saved order, a missing value is a real bug and an error helps you find it. Adding ?. would hide the bug and pass undefined further along. Use ?. only where a value is genuinely optional.

?. cannot be used on the left of =. user?.address = {} is a syntax error. Optional chaining is for reading, not for assigning.

Common mistakes

  • Using || for numbers that can be 0. A score, price or count of 0 gets replaced. Use ??.
  • Writing ?. after a variable that was never declared. ?. protects against null and undefined values, not against a missing variable name, which still throws a ReferenceError.
  • Forgetting the dot with brackets and calls. It is list?.[0] and fn?.(), not list?[0].
  • Mixing ?? and || without brackets. Add brackets to show which runs first.

Key points

  • a ?? b gives b only when a is null or undefined; 0, "" and false are kept.
  • a || b replaces every falsy value, which is often too much for numbers and strings.
  • obj?.prop, obj?.[key] and fn?.() give undefined instead of throwing when the left side is nullish.
  • ??=, ||= and &&= assign only under their condition.
  • ?? cannot be mixed with || or && without brackets.
  • Use ?. only where a value is truly optional, so real bugs still show up.

Exercise

You receive this order data from a server: const order = { id: 501, customer: { name: "Isha" }, tip: 0 };. Print the customer's name, the customer's phone number (print "No phone" if missing), the delivery city from order.delivery.city (print "Pickup" if missing), and the tip (it should print 0, not a default).

Show solution
JavaScript
const order = { id: 501, customer: { name: "Isha" }, tip: 0 };

console.log(order.customer?.name ?? "Unknown");
console.log(order.customer?.phone ?? "No phone");
console.log(order.delivery?.city ?? "Pickup");
console.log(order.tip ?? 20);
Output
Isha
No phone
Pickup
0

order.delivery is missing, so ?. stops before .city and ?? supplies "Pickup". The tip is 0, which is not nullish, so ?? keeps it.

Quick Quiz

1. What does 0 ?? 25 give?

2. What does const p = null; console.log(p?.name); print?

3. After let count = 0; count ||= 10;, what is count?

4. Which line is a syntax error?

Learn JavaScript with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call