The type system beyond annotations: narrowing, generics, utility types, and how to model real domains.
Write async function safeDivide(a: number, b: number): Promise<{ ok: true; value: number } | { ok: false; error: string }>
that resolves to { ok: true, value: a / b }, or, when b is 0,
resolves to { ok: false, error: "Division by zero" } instead of
throwing.
Write async function runPipeline(steps: number[], failOn: number): Promise<{ results: number[]; error: string | null }>.
Process steps one at a time, in order, awaiting an async step that
doubles the value — but if a step's value equals failOn, that step
throws a custom PipelineError (carrying the step's index and a
What does the return type of an async function always look like? Why
can't an async function just return T directly?
In a try/catch around an await, what type does the caught error
have in modern TypeScript, and how do you use it safely?
Write a BankAccount class with a private balance starting at 0,
deposit(amount: number): void, and withdraw(amount: number): boolean
that subtracts amount and returns true only if the balance is
sufficient (otherwise it changes nothing and returns false). Then
Write an abstract class Shape with abstract area(): number, and
concrete subclasses Circle (constructed with a radius) and
Rectangle (constructed with width and height). Then write
totalArea(shapes: ({ kind: "circle"; radius: number } | { kind: "rectangle"; width: number; height: number })[]): number