Nullish Coalescing & Optional Chaining
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.
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");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 side | left || "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.
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");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:
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?.());Masala dosa undefined Out for delivery undefined
items?.[0]reads the first item only ifitemsexists.track?.()calls the function only if it exists; otherwise the result isundefined.
Logical assignment: ??=, ||= and &&=
These operators combine a check with an assignment. They are handy for filling in defaults on an existing object.
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);Not set 0 English ROHAN
| Operator | Assigns only when the variable is | Same as |
|---|---|---|
a ??= b | null or undefined | a ?? (a = b) |
a ||= b | falsy | a || (a = b) |
a &&= b | truthy | a && (a = b) |
Rules and good habits
You cannot mix ?? with || or && without brackets. JavaScript refuses to guess which you meant and throws a SyntaxError:
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); // 0Do 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 of0gets replaced. Use??. - Writing
?.after a variable that was never declared.?.protects againstnullandundefinedvalues, not against a missing variable name, which still throws aReferenceError. - Forgetting the dot with brackets and calls. It is
list?.[0]andfn?.(), notlist?[0]. - Mixing
??and||without brackets. Add brackets to show which runs first.
Key points
a ?? bgivesbonly whenaisnullorundefined;0,""andfalseare kept.a || breplaces every falsy value, which is often too much for numbers and strings.obj?.prop,obj?.[key]andfn?.()giveundefinedinstead 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
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);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?
?? only falls back for null and undefined, and 0 is neither.
2. What does const p = null; console.log(p?.name); print?
When the left side of ?. is nullish, the whole expression gives undefined instead of throwing.
3. After let count = 0; count ||= 10;, what is count?
||= assigns when the value is falsy, and 0 is falsy. ??= would have kept the 0.
4. Which line is a syntax error?
?? cannot be combined with || or && unless brackets show the order.
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
