Read Something That Might Not Be There
JavaScript Optional Chaining
The `?.` operator safely reads properties on values that might be `null` or `undefined` — no crash, no manual checks.
What you'll learn
- Use `?.` to safely read deep properties
- Combine with `??` for clean defaults
- Use `?.()` for optional method calls
When you try to read a property of null or undefined, JavaScript
throws a TypeError. The optional chaining operator ?. makes
the read return undefined instead.
const user = null;
// console.log(user.name); // TypeError: Cannot read properties of null
console.log(user?.name); // undefined ← safe, no crash Chaining Through Nested Properties
The real power: chains. If anything along the way is null or
undefined, the whole expression evaluates to undefined.
const user = {
name: "Ada",
address: {
city: "London",
},
};
console.log(user?.address?.city); // "London"
console.log(user?.address?.zipCode); // undefined (no zipCode property)
console.log(user?.profile?.bio); // undefined (no profile)
console.log(user?.profile?.bio?.length); // undefined (chain stops at profile) Compare with the old way:
// Without ?.:
const city = user && user.address && user.address.city;
// With ?.:
const city = user?.address?.city; Combine With ?? for Defaults
?. returns undefined when something’s missing. Pair with ?? for
a default value.
const user = { name: "Ada" };
const city = user?.address?.city ?? "unknown";
console.log(city); // "unknown" Optional Method Calls With ?.()
?.() calls a function only if it exists. If it’s null or
undefined, the call is skipped (returns undefined).
const obj = {
greet() { return "hello!"; },
};
console.log(obj.greet?.()); // "hello!"
console.log(obj.missingMethod?.()); // undefined ← no crash This is great for optional callbacks:
function doWork(onProgress) {
// call only if a callback was passed
onProgress?.(50);
} Optional Array Access With ?.[]
Same idea for [index] lookups when the array itself might be
missing.
function firstUserName(users) {
return users?.[0]?.name ?? "no users";
}
console.log(firstUserName([{ name: "Ada" }])); // "Ada"
console.log(firstUserName(null)); // "no users"
console.log(firstUserName([])); // "no users" What ?. Does NOT Do
- It doesn’t silently swallow all errors — only the
null/undefinedcase. Calling a non-function still throws. - It doesn’t catch typos.
user?.adressreturnsundefined, the same as a missing property.
Up Next
You’ve covered all the major decision-making tools. Time for loops.
JavaScript for Loops →