Quiz - Functions, naming, and control flow
You’re writing a helper that’s used by code earlier in the same module, and a separate type predicate isInvoice(x: unknown): x is Invoice. Which forms does a 2026 senior reach for?
const arrow expressions — arrow is the default, no exceptions.function declaration (hoisting trigger); the type predicate as a function declaration (signature trigger).function expression assigned to const.const is the default, but three triggers earn function: hoisting (so the binding is callable before its declaration line), named recursion, and TypeScript predicate/assertion signatures. The first trigger covers the early-use helper; the third covers the predicate — x is T is conventionally declared with function, and asserts x is T only parses on a function form.What does this snippet print?
const list = (page: number = 1, size: number = 20) => console.log(`p=${page} s=${size}`);
list(undefined, 0);list(null as unknown as number, 10);p=1 s=20 then p=1 s=10p=1 s=0 then p=null s=10p=1 s=0 then p=1 s=10undefined. The first call: page is undefined so the default 1 fires; size is 0 (falsy but defined) so the default does not fire and 0 flows through. The second call: null is not undefined, so the page default does not fire and null is interpolated; size is explicitly 10.Which of these names violate the “name for intent, not implementation” principle? Select all that apply.
customerArraypendingInvoicesnotDisableddatacustomerArray leaks the container — rename the type and the name lies. notDisabled is a negated boolean that compounds with ! at use sites into a double negative; name the positive condition (isEnabled). data is a vague abstraction that fits anything and communicates nothing. pendingInvoices is fine — concrete, intent-revealing, no container suffix.Under noFallthroughCasesInSwitch with the discriminated union type Payment = { status: 'pending' } | { status: 'paid' } | { status: 'failed' }, which switch compiles?
switch (p.status) { case 'pending': log('p'); case 'paid': return 'paid'; case 'failed': return 'failed'; default: return assertNever(p);}switch (p.status) { case 'pending': return 'p'; case 'paid': return 'paid'; default: return assertNever(p);}switch (p.status) { case 'pending': throw new Error('pending'); case 'paid': return 'paid'; case 'failed': return 'failed'; default: return assertNever(p);}case 'pending' runs log('p') without a terminator, so noFallthroughCasesInSwitch errors on the fallthrough. The second fails exhaustiveness: 'failed' has no case, so at default p is still { status: 'failed' }, not never, and assertNever(p) is rejected. The third compiles — throw is a valid terminator, every variant has a case, and p narrows to never at the default.A teammate writes const pageSize = input.pageSize || 20 and const city = user?.profile.address.city. Which fixes does a 2026 senior land in review? Select all that apply.
|| to ?? so that a caller passing pageSize: 0 (meaning “show no rows”) isn’t silently swapped for 20.?. at every nullable link — user?.profile?.address?.city — because each ?. only guards the one access to its left.|| alone; in TypeScript with strict mode, || and ?? behave identically.|| fires on every falsy value (0, '', false, plus nullish), so the pageSize: 0 case is swallowed; ?? fires only on nullish. Optional chaining short-circuits one link at a time — the original chain still throws at .address if profile is nullish. Strict mode does not change runtime operator semantics.After const { foo: bar } = obj, what’s true?
foo is now a local binding holding obj.bar.bar is now a local binding holding obj.foo. foo is not in scope.foo and bar are in scope, both holding obj.foo.: local-binding. The source field is foo; the local binding the line creates is bar. This is the inverse direction from object-literal shorthand, which is why it’s the most mis-read piece of destructuring syntax — and why flipping it ships shadow bugs.A Server Action file does const user = await getCurrentUser() at module scope and then references user inside the exported action body. What goes wrong?
getCurrentUser() runs once at module load, and every later request closes over that same user binding — authorization checks pass or fail based on whoever first loaded the module, not the current caller.user.user binding sees whatever value it held at load time — not per-request data. The fix is to read request-scoped APIs (getCurrentUser, cookies(), headers()) inside the action body, so each invocation resolves its own request context. Same closure model as the stale-closure-in-loop bug, surfacing in a different production site.Quiz complete
Score by topic