src/lib/utils.ts — assertNever and indexes
In the code column — src/lib/utils.ts with assertNever, toDateParam and a generic debounce.
noUncheckedIndexedAccess in practice — toDateParam:
const iso = date.toISOString().split('T')[0]; // string | undefined
if (iso === undefined) throw new Error('Could not format date');
return iso; // now just string
split('T')[0] almost always exists — but the type doesn't guarantee it, so the compiler forces you to handle undefined. One line more, but instead of expecting, you now know.
assertNever — exhaustiveness. Picture a switch over LoadStatus:
function label(status: LoadStatus): string {
switch (status.state) {
case 'idle': return '';
case 'loading': return 'Loading…';
case 'loaded': return 'Done';
case 'error': return status.message;
default: return assertNever(status); // ← all variants handled
}
}
As long as all four variants are handled, in the default branch status is never, and assertNever(status) compiles. Add a fifth variant (say { state: 'stale' }) — status in the default branch is no longer never, and assertNever stops compiling, pointing at exactly this switch.
Verify.
1. Add a 'refreshing' variant to LoadStatus. `npm run typecheck` immediately
points at EVERY switch that doesn't handle it.
2. arr[0] without a check — error "possibly undefined".
That's strictness's gift to a growing app: change a type, and the compiler becomes your "to-do list" — it shows every place that needs updating. Otherwise you'd hunt by hand and hope you missed nothing.
Tip (generics).
debounce<Args extends unknown[]>preserves the argument types of the function you pass it. A non-generic(fn: Function)would "drop" them, and you'd lose checking at the call site. Generics let you write reusable code without losing types — what lesson 7's vanilladebouncedid with no guarantees at all.