Quiz - Typing values you know
A webhook handler receives a payload whose shape isn’t known at compile time. Two engineers reach for different annotations:
const handleA = (payload: any) => payload.user.email;const handleB = (payload: unknown) => payload.user.email;Which best describes the operational difference between the two?
handleB errors at runtime.handleA compiles and may crash at runtime; handleB fails at compile time until the value is narrowed.handleB compiles but disables type-checking on payload; handleA requires narrowing before any read.any disables type-checking on every read off the value, so handleA compiles and ships — and crashes the moment the payload’s shape doesn’t match. unknown is the sound top: it accepts every value but refuses every read until the code narrows the shape the compiler can see. That refusal is the feature.The course defaults to type for every alias. Which scenario is the one trigger that earns a reach for interface instead?
Session) inside a declare module block.readonly modifiers.type can’t do — two type declarations with the same name are a duplicate-identifier error. The canonical use is augmenting a type a package shipped via declare module plus an interface. Composition uses &; modifiers work identically on either form.useState returns [value, setter] instead of { value, setValue }. What’s the senior reason the tuple shape wins here?
[isOpen, toggleOpen] and [isHovered, toggleHover] from the same hook without collision. Past two slots, names win and the object is the senior call.You’re typing a lookup table from invoice status to display label. The status is 'draft' | 'sent' | 'paid'. Under noUncheckedIndexedAccess, which type catches a missing key at the literal site and reads back as string (not string | undefined)?
Record<string, string>{ [status: string]: string }Record<'draft' | 'sent' | 'paid', string>V directly (no | undefined) because every key in the union is guaranteed present. The Record<string, ...> and index-signature forms both admit any string and force a | undefined at every read.A function takes value: User | Guest. User has an email field; Guest doesn’t. You need to read email when it exists. Which moves are correct senior reflexes? Select all that apply.
if ('email' in value) before the read.{ email?: string; name: string } so the read compiles.(value as User).email — you know the call sites only pass users with emails.in, or equality on a discriminant). Widening hides the bug; asserting lies to the compiler — the runtime value is unchanged, and a real Guest will crash the next line that touches email.Which of the following is not one of the three legitimate triggers for reaching for as?
userSchema.parse(raw) boundary where the parser validates unknown and returns a typed value.document.querySelector('button[type="submit"]') as HTMLButtonElement in a tightly-scoped one-shot.User | Guest parameter to User because most call sites pass users.cache.get(id) as User immediately after cache.set(id, user), where the type system can’t track the round-trip.User is the opening bug of the lesson — the runtime value is unchanged, and the first real Guest will crash the next line.Consider the four scenarios below. Which calls for the combined as const satisfies T idiom?
(input: { email: string }) => void.RouteName and whose values must remain literal paths so (typeof ROUTES)[keyof typeof ROUTES] narrows.reduce over a list of numbers.as const keeps the literal paths so (typeof ROUTES)[keyof typeof ROUTES] reads the narrow union; satisfies Record<RouteName, string> checks completeness without re-widening. An annotation on the same value would erase the literals; as const alone would skip the contract check.Which line is the wrong reach under the boundary rule and verbatimModuleSyntax?
const total = items.reduce((acc, item) => acc + item.price, 0);export const createInvoice = async ( input: CreateInvoiceInput,): Promise<Result<Invoice>> => { /* ... */ };import { User } from './user';
const greet = (user: User) => `Hello, ${user.name}`;verbatimModuleSyntax, a value import for a name used only as a type is a compile error — write import type { User } so the compiler erases the import and the bundler never sees it. The reduce is the canonical “infer the inside” case; the exported async function annotates parameters and return type at the boundary, exactly where the rule wants them.Quiz complete
Score by topic