← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Optional Chaining & Nullish Coalescing

JavaScript ES2020 Intermediate
debt(d3/e1/b1/t5)
d3 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'default linter catches the common case' (d3). ESLint and TypeScript (both listed in detection_hints.tools) automatically flag unsafe property access chains and missing optional chaining. However, the distinction between ?. and && chains isn't universally caught by default configs, so detection requires some linter tuning rather than instant compiler error.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch or single-call swap' (e1). The quick_fix directly states replacing verbose null checks (user && user.address && user.address.city) with optional chaining (user?.address?.city) is a single-line substitution. No architectural change or multi-file refactor needed.

b1 Burden Structural debt — long-term weight of choosing wrong

Closest to 'minimal commitment' (b1). Optional chaining is a pure syntax feature with no structural consequences. It applies locally to individual property accesses; once a developer learns the syntax, it imposes no ongoing tax on the codebase or future maintainers. It's a naming/syntax convention choice, not a system-wide commitment.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly documents the trap: ?? is not the same as ||. The common mistake list reinforces this (using || when ?? is correct for 0 and false values). This is a well-documented gotcha that catches developers switching from other languages or unfamiliar with nullish coalescing semantics, but it's discoverable through code review and testing.

About DEBT scoring →

Also Known As

?. ?? optional chaining nullish coalescing

TL;DR

?. short-circuits to undefined if a property is null/undefined; ?? returns the right-hand value only when the left is null or undefined — together they replace most null guard code.

Explanation

Optional chaining (obj?.prop, obj?.method(), arr?.[0]) returns undefined instead of throwing TypeError when accessing properties on null or undefined. Nullish coalescing (??) returns the right operand only when the left is null or undefined — unlike || which also triggers for 0, '', and false. Together they handle optional API response properties, default values, and deep object access safely without nested ternaries or &&-chains.

Common Misconception

?? is the same as || for defaults — || replaces any falsy value (0, '', false); ?? only replaces null and undefined, making it correct for values where 0 or '' are valid.

Why It Matters

?? prevents a common bug where 0 or empty string as a valid value gets replaced by || with a default — subtly breaking numeric counters, empty-string usernames, and boolean false values.

Common Mistakes

  • Using || for defaults when 0 or false are valid values — use ?? instead.
  • Not combining ?. with ?? for deep access with a fallback: user?.address?.city ?? 'Unknown'.
  • Using ?. on every access in a chain when only the first link can be null — unnecessary and misleading.
  • Not using ?.() for optional method calls: user.formatter?.() instead of user.formatter && user.formatter().

Code Examples

✗ Vulnerable
// || replaces valid falsy values:
const port = config.port || 3000; // port = 0 → uses 3000 (bug!)
const name = user.name || 'Anonymous'; // name = '' → uses 'Anonymous' (maybe wrong)

// Verbose null guards:
const city = user && user.address && user.address.city;
✓ Fixed
// ?? only replaces null/undefined:
const port = config.port ?? 3000;  // port = 0 → keeps 0
const name = user.name ?? 'Anonymous'; // name = '' → keeps ''

// Optional chaining + nullish coalescing:
const city = user?.address?.city ?? 'Unknown';
const len  = data?.items?.length ?? 0;

Added 15 Mar 2026
Edited 22 Mar 2026
Views 66
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 2 pings T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W
PetalBot 1
No pings yesterday
Amazonbot 7 Perplexity 7 SEMrush 7 ChatGPT 6 Ahrefs 5 Scrapy 4 PetalBot 3 Unknown AI 2 Majestic 1 Google 1 Meta AI 1 Twitter/X 1 Bing 1 Applebot 1
crawler 42 crawler_json 4 pre-tracking 1
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
JavaScript javascript The programming language of the browser — it reads and modifies the page (the DOM), reacts to user events, and fetches data without reloading.

JavaScript is the only language browsers execute, so every interactive behaviour on the web goes through it. Its two defining traits — single-threaded event loop and loose typing (== coercion) — explain the majority of both its bugs and its design patterns.

💡 Default to const, use === always, and reach for let only when a value genuinely reassigns.

Ask Codex about JavaScript →
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Use optional chaining (?.) to safely access nested properties: user?.address?.city — it short-circuits to undefined instead of throwing TypeError when any part of the chain is null/undefined
📦 Applies To
javascript ES2020 web cli
🔗 Prerequisites
🔍 Detection Hints
if (user && user.address && user.address.city) verbose null checks; TypeError from accessing property of null/undefined
Auto-detectable: ✓ Yes eslint typescript
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✓ Auto-fixable Fix: Low Context: Line


✓ schema.org compliant