Checkpoint: Reusable Lookup

The Course
•
4 min read

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.

Predict, then runTypeScript
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 tryWhat your result should demonstrate
Session id matchRetain session-specific fields
Book id matchRetain book-specific fields
Missing idReturn 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

Exercise

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.

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

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

Was this article helpful?

Your response is saved on this device.