Optional chaining (?.) and nullish coalescing (??) help JavaScript handle missing values without long checks or accidental fallbacks. The common trap is treating them as interchangeable with ||: they behave differently when a value is 0, false, or an empty string. This guide shows how each operator works, what it skips, and which combinations produce syntax errors.
Key Takeaways
- Use
?.to stop a property access or call when the value immediately to its left isnullorundefined. - Use
??to supply a fallback only fornullorundefined. - Use
||only when every falsy value—including0,false, and""—should trigger the fallback. - Optional chaining short-circuits part of an expression; it does not validate data or make every later access safe.
- Parenthesize when mixing
??with&&or||; unparenthesized combinations are invalid syntax.
What Optional Chaining Does
A property access such as user.profile.name throws if user or user.profile is null or undefined. Optional chaining lets the expression return undefined instead when the value directly before ?. is nullish:
const user = null;
console.log(user?.profile); // undefined
console.log(user?.profile.name); // undefined
In the second expression, the chain continues through .name only if user?.profile did not short-circuit. But if user exists and profile is nullish, the next ordinary .name access throws. Use another ?. when that intermediate value can also be missing:
const user = { profile: null };
console.log(user?.profile?.name); // undefined
Optional chaining also works with bracket access and function calls:
const settings = {};
const key = "theme";
console.log(settings?.[key]); // undefined
const service = {};
console.log(service.load?.()); // undefined: load is missing
service.load?.() checks whether load is nullish before calling it. It does not silently ignore a non-function value: if load is, for example, a string, calling it still throws a TypeError.
?? Versus ||
Both operators can provide a default, but their conditions differ:
value ?? fallbackuses the fallback only whenvalueisnullorundefined.value || fallbackuses the fallback whenvalueis any falsy value, including0,false,"",NaN,null, orundefined.
Here is a runnable example for the requested values:
const cases = [
["null", null],
["undefined", undefined],
["zero", 0],
["false", false],
["empty string", ""]
];
for (const [label, value] of cases) {
const optionalResult = value?.toString();
const nullishResult = value ?? "fallback";
const orResult = value || "fallback";
console.log(
`${label}: optional=${String(optionalResult)}, ` +
`??=${String(nullishResult)}, ||=${String(orResult)}`
);
}
Output:
null: optional=undefined, ??=fallback, ||=fallback
undefined: optional=undefined, ??=fallback, ||=fallback
zero: optional=0, ??=0, ||=fallback
false: optional=false, ??=false, ||=fallback
empty string: optional=, ??=, ||=fallback
The optional-chain result is undefined for null and undefined because the method call is skipped. For the other values, toString() runs. The ?? and || columns show why || can accidentally replace valid values such as a count of 0, a false setting, or an intentionally empty string.
For a default that should apply only when a setting is absent, prefer ??:
const retries = config.retries ?? 3;
If config.retries is 0, the result remains 0. With config.retries || 3, it would become 3.
How Short-Circuiting Works
When the value on the left of ?. is nullish, JavaScript skips the rest of that optional chain. That includes computed property expressions and call arguments within the skipped part:
let index = 0;
const list = null;
const item = list?.[index++];
console.log(item); // undefined
console.log(index); // 0: the index expression was not evaluated
Short-circuiting does not automatically protect expressions outside the chain. Parentheses can end the chain:
const user = null;
console.log((user?.profile).name); // TypeError: reading name from undefined
Avoid adding parentheses in a way that turns a safely chained access into an ordinary access unless you handle the possible undefined result.
Invalid Syntax Combinations
JavaScript does not allow an unparenthesized mixture of ?? with && or ||. These expressions are syntax errors:
// SyntaxError: parentheses are required
// const result = primary ?? backup || "default";
// const enabled = setting ?? isEnabled && fallback;
Add parentheses to make the intended grouping explicit:
const result = (primary ?? backup) || "default";
const enabled = setting ?? (isEnabled && fallback);
Choose the grouping based on the desired behavior; parentheses do not make the operators equivalent. Optional chains also cannot be assignment targets, so object?.property = value is invalid. Check the object before assigning instead:
if (object != null) {
object.property = value;
}
Common Mistakes
Using || when zero or false is meaningful
A setting such as volume = 0 may be valid. volume || 50 replaces it; volume ?? 50 preserves it.
Assuming ?. checks every value in an expression
account?.profile.name protects the access on account, but can still throw if profile is nullish. Use account?.profile?.name when either value may be missing.
Confusing an optional call with an optional method lookup
object.method?.() skips the call if method is nullish. object?.method() only makes the access to object optional; if object exists but method is missing, the call throws. Choose the form that matches which value may be absent.
Expecting undeclared variables to be safe
missingVariable?.property still throws a ReferenceError if missingVariable was never declared. Optional chaining handles a nullish value, not an unknown identifier.
Using optional chaining as validation
response?.user?.name can avoid an exception, but it does not prove that the response has the expected shape or type. Validate untrusted data when the application depends on its contents.
Best Practices
- Use
?.at each boundary where a value may legitimately benullorundefined. - Use
??for defaults that should preserve0,false, and"". - Keep
||when the intended rule is “use the fallback for any falsy value.” - Make mixed-operator grouping explicit with parentheses.
- Handle required data with validation or an explicit error rather than letting optional chaining conceal an unexpected missing value.
Conclusion
Optional chaining safely stops a chain at a nullish value; nullish coalescing supplies a fallback only for null and undefined. Keeping those behaviors distinct from || helps prevent both avoidable errors and accidental replacement of valid falsy values.
