Combining ?. and ??
Pair optional chaining with sensible default values.
Combining ?. and ?? is a free TypeScript Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
A Perfect Pairing
Optional chaining and nullish coalescing are designed to work together. The pattern obj?.value ?? fallback safely reads a nested value and supplies a default if it's missing. It's one of the most useful idioms in modern TypeScript.
type User = { profile?: { name?: string } };
const u: User = {};
const name = u.profile?.name ?? 'Anonymous';
console.log(name);How the Pattern Works
First ?. evaluates the chain, yielding the value or undefined. Then ?? checks that result: if it's nullish, the fallback is used. Two operators, one clean expression.
type Cfg = { server?: { port?: number } };
const cfg: Cfg = { server: {} };
const port = cfg.server?.port ?? 8080;
console.log(port);The Type of the Result
The chain produces T | undefined; the ?? removes undefined and unions the fallback's type. With a matching fallback, the final type is just T — no undefined left to handle.
type Cfg = { timeout?: number };
const cfg: Cfg = {};
const timeout: number = cfg.timeout ?? 30;
console.log(timeout + 5); // number, safe arithmeticPreserving Valid Falsy Values
Because ?? only triggers on nullish, a legitimate 0 retrieved through the chain is preserved. The fallback applies only when the property is genuinely missing.
type Audio = { settings?: { volume?: number } };
const a: Audio = { settings: { volume: 0 } };
console.log(a.settings?.volume ?? 50); // 0, not 50Deep Config Access
For deeply nested configuration, chain multiple ?. steps and finish with a single ??. Any missing link cleanly falls back to the default.
type Cfg = { db?: { pool?: { max?: number } } };
const cfg: Cfg = { db: {} };
const maxPool = cfg.db?.pool?.max ?? 10;
console.log(maxPool);API Response Defaults
API responses often omit optional fields. The combined pattern reads them defensively and substitutes sensible defaults, keeping downstream code simple and crash-free.
type Res = { data?: { items?: string[] } };
const res: Res = { data: {} };
const items = res.data?.items ?? [];
console.log('Item count:', items.length);Combining With Method Results
You can chain off function or method calls and default their results. If the call returns nullish, the fallback steps in.
type Store = { find?: () => string | undefined };
const store: Store = { find: () => undefined };
const result = store.find?.() ?? 'default';
console.log(result);Avoiding Verbose Guards
Before these operators you'd write multi-line null checks. The combined idiom replaces a whole block with a single expression, improving readability without losing safety.
type User = { name?: string };
const u: User = {};
// Old: let n; if (u && u.name) n = u.name; else n = 'Guest';
const n = u?.name ?? 'Guest';
console.log(n);Fallback to Another Lookup
The fallback after ?? can itself be another chained lookup, letting you try a secondary source before a final literal default.
type Prefs = { theme?: string };
const userPrefs: Prefs = {};
const sitePrefs: Prefs = { theme: 'light' };
const theme = userPrefs?.theme ?? sitePrefs?.theme ?? 'dark';
console.log(theme);Combining With Element Access
The pattern works with array element access too: read an element through ?.[i] and default it with ??.
type Data = { tags?: string[] };
const d: Data = { tags: [] };
const first = d.tags?.[0] ?? 'none';
console.log(first);A Real-World Settings Reader
Putting it together: a settings reader that safely descends through optional layers and guarantees a usable value. This is production TypeScript you'll write often.
type Settings = { ui?: { lang?: string } };
function getLang(s: Settings): string {
return s.ui?.lang ?? 'en';
}
console.log(getLang({ ui: { lang: 'tr' } }), getLang({}));Quick Check
Test your understanding of combining ?. and ??.
Recap: Combining ?. and ??
You learned the idiom obj?.value ?? fallback:
?.safely descends, yielding the value orundefined.??supplies a default only when the result is nullish.- The final type drops
undefined, often becoming a cleanT. - Valid falsy values like
0are preserved.
Next, optional calls and optional element access.
type Cfg = { limit?: number };
const c: Cfg = {};
const limit = c?.limit ?? 100;
console.log(limit);Frequently asked questions
Is the “Combining ?. and ??” lesson free?
Yes — the full text of “Combining ?. and ??” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.
What will I learn in “Combining ?. and ??”?
Pair optional chaining with sensible default values. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start TypeScript Academy?
No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Combining ?. and ??” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this TypeScript Academy lesson?
Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.