Build one small helper that works for two kinds of records. A session has id, topic and minutes. A book has id, title and pages. Both can be looked up by a string id, but callers should still know which full record they receive.
Write a constrained generic findRecord instead of separate session and book algorithms. The return must include undefined because no match is possible. At the call site, narrow or use optional chaining before reading the domain field.
Do not choose any as the return type. That would let a caller ask for title on a session without a diagnostic, throwing away the relationship the helper is supposed to preserve. Likewise, returning only { id: string } would discard useful known fields.
Spend the final five minutes explaining why the generic is justified here. The code is short; the learning goal is the contract. You should be able to say what T preserves, what the constraint requires, and what happens on an unsuccessful search.
Predict, check, then run
Before running, write down the output and explain which information the checker knows. Click Check types first, then Check & run.
function echo<T>(value: T): T {
return value;
}
const session = echo({ id: "s1", topic: "Types" });
console.log(session.topic);Enable JavaScript to edit and type-check this example.
Check your prediction
Types Acceptance cases before you code
Write a small test plan before implementing generic lookup relationship. 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 |
|---|---|
| Session id match | Retain session-specific fields |
| Book id match | Retain book-specific fields |
| Missing id | Return undefined and handle it without a cast |
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.
Your turn
Checkpoint: Reusable Lookup: practise
Write findRecord<T extends { id: string }>(items: readonly T[], id: string): T | undefined. Test a session, a book, and a missing book. Print Types, Handbook and Missing. Access the domain fields after lookup without assertions or any.
// Define the helper once. Demonstrate two record shapes and an absent id.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
The constraint gives access to id, while the type parameter preserves the rest.
Hint 2: a first step
Use find with a predicate comparing each item’s id to the requested id.
Show one solution
function findRecord<T extends { id: string }>(
items: readonly T[],
id: string
): T | undefined {
return items.find(item => item.id === id);
}
const sessions = [{ id: "s1", topic: "Types", minutes: 25 }];
const books = [{ id: "b1", title: "Handbook", pages: 120 }];
console.log(findRecord(sessions, "s1")?.topic ?? "Missing");
console.log(findRecord(books, "b1")?.title ?? "Missing");
console.log(findRecord(books, "none")?.title ?? "Missing");Enable JavaScript to edit and type-check this example.
Types
Handbook
MissingExplain the types at the function boundary and the runtime checks, if any. Compare your approach before copying this answer.
Change it and explain it
Test an empty array with an explicit record element type. Try reading title from a found session and confirm the checker rejects it. Explain why a reusable helper must not assume every record with an id is a book.
Completion is saved in this browser only. It does not measure mastery.