Build one complete registry from memory. The event map has an added payload with title and a removed payload with id. Each formatter must receive only its own payload. Your runtime object must implement both keys.
This checkpoint is intentionally smaller than a generic dispatcher. Calling the known added and removed formatters directly preserves the relationship without an assertion. Tomorrow we introduce validated domain values; Day 4 handles the trickier job of dispatching a union event.
Spend twenty minutes on the contract, two positive calls and a negative compiler experiment. A clear two-key implementation is better evidence than a complicated helper that hides missing branches.
Predict, check, and run
Write the output before running. Use Check types first, then Check & run. Types disappear at runtime: explain both what the compiler checks and what the code actually does. Each editor is an independent ES module; await asynchronous work explicitly.
type Events = {
added: { title: string };
removed: { id: string };
};
type Names = keyof Events;
const name: Names = "added";
console.log(name);Enable JavaScript to edit and type-check this example.
Check your prediction
added Acceptance cases before you code
Write a small test plan before implementing formatter coverage and relationships. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.
| Case to try | What your result should demonstrate |
|---|---|
| Every current event key | Has one implementation |
| Callback for removed payload | Cannot demand an added-only title |
| Add a renamed key | Coverage checking fails until the registry deliberately implements it |
After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.
Build it yourself
Checkpoint: Event Formatters: practise
Build a mapped Formatters<E> and a complete registry satisfying it. Format added title Types as Added Types and removed id b1 as Removed b1. Try adding a renamed event and confirm coverage fails until implemented.
// Start from the stated contract. Add a valid call and an edge case.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
Each property value is a function from E[K] to string.
Hint 2: a first step
Use satisfies on the two-key object rather than widening every payload into one union.
Show one solution
type Events = {
added: { title: string };
removed: { id: string };
};
type Formatters<E> = { [K in keyof E]: (payload: E[K]) => string };
const format = {
added: book => `Added ${book.title}`,
removed: book => `Removed ${book.id}`
} satisfies Formatters<Events>;
console.log(format.added({ title: "Types" }));
console.log(format.removed({ id: "b1" }));Enable JavaScript to edit and type-check this example.
Added Types
Removed b1Trace the implementation against the task before comparing styles. A passing example supports that case; use the changes below to challenge the wider contract.
Change it and test it
Try reading title inside removed, omit a key, and pass the wrong payload at a call site. Write down the distinct diagnostic in each case. Restore a clean check before continuing.
Completion is saved in this browser only. It does not measure mastery.