Trust is earned, not given

A different perspective

2020-02-11 · Projects

Node.js, part 9: optional chaining and nullish coalescing — the defensive code you can delete

Part 9from the Node.js series · 24 parts in all

Node 14 landed V8 8.1 and with it two operators that had been stuck in proposal limbo for years: ?. and ??. They look like syntax sugar and they are — but they delete a category of code whose absence is hard to notice, because defensive && chains hide the difference between "missing" and "false".

Optional chaining stops at the first null

// Before: every level is a chance for "Cannot read property of undefined".
const city = user && user.address && user.address.city;

// After: short-circuits anywhere in the chain.
const city = user?.address?.city;

It works on calls too, which is the case that used to need a type check: logger?.debug?.('starting') runs nothing if either is missing. One rule to keep straight, because it is the usual bug: a?.b short-circuits the whole chain to the right of it, but (a?.b).c does not — the parentheses end the short-circuit and .c throws.

Nullish coalescing is about null, not falsiness

This is the part that is not just shorter:

const port = config.port || 3000;    // 0 and '' and false also become 3000
const port = config.port ?? 3000;    // only null / undefined become 3000

// The bug the extra character fixes: a legitimate 0 retry count,
// an empty-but-intentional string, a disabled flag.
const retries = cfg.retries ?? 3;

Those two lines are the whole reason the operator needed its own syntax. || conflates "absent" with "falsy", and for numbers and booleans that distinction is real.

Two companions worth learning at the same time

const name = user.nickname ?? user.fullName ?? 'anonymous';

// Assign only if currently null/undefined.
settings.timeout ??= 5000;

// Call only if the function exists.
handler?.(payload);

Together these turn a validation-heavy function into a readable one. The caution that comes with them: ?. makes missing data silent, so if a null is a bug, an explicit check that throws will save you more time than the operator saves characters. Next: request-scoped context, the problem that used to require threading a logger through every function signature.