Checkpoint: Event Formatters

The Course
•
3 min read

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.

Predict, then runTypeScript
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 tryWhat your result should demonstrate
Every current event keyHas one implementation
Callback for removed payloadCannot demand an added-only title
Add a renamed keyCoverage 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

Exercise

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.

Your workspaceTypeScript
// 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
One possible solutionTypeScript
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 b1

Trace 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.

Was this article helpful?

Your response is saved on this device.