Sometimes you need a value by name rather than by position. Record<Keys, Value> is a built-in helper type for an object whose specified keys have the given value type. The angle brackets supply information to a reusable type; Day 5 will explain how to write such generics yourself.
Record<"planned" | "done", string> requires both of those keys with string values. A union of literal keys is a closed vocabulary, useful for complete label tables. Missing a key is a type error. Indexing with a key from that same union gives a string.
Record<string, number> describes an open dictionary. It cannot guarantee that an arbitrary runtime key exists. With this course’s unchecked-index protection, a lookup can return undefined. Supply a default or branch on absence before arithmetic.
Prefer a small literal-key union when the possible keys are known. Use an open dictionary when names genuinely arrive dynamically. These examples use controlled keys. Validating arbitrary external keys is a separate boundary task, not something a Record annotation does for you. Do not read a type like Record<string, number> as permission to trust external data.
The type helper produces no object at runtime. You still create a plain JavaScript object, and the same rules for property access apply. Types make the intended role visible.
Predict, check, then run
Before running, write down the output and explain which information the checker knows. Click Check types first, then Check & run.
type Status = "planned" | "done";
const labels: Record<Status, string> = { planned: "Next", done: "Complete" };
function statusLabel(status: Status): string {
return labels[status];
}
console.log(statusLabel("done"));
const minutes: Record<string, number> = { Types: 25 };
console.log(minutes["Unknown"] ?? 0);Enable JavaScript to edit and type-check this example.
Check your prediction
Complete
0 Read a real compiler error
A closed record promises an entry for every named key, even if this run only reads Mon.
type Day = "Mon" | "Tue";
const minutes: Record<Day, number> = { Mon: 10 };
console.log(minutes.Mon);Enable JavaScript to edit and type-check this example.
Check your diagnosis
Add a numeric Tue entry. If the real model allows absent days, use a different contract and explicitly handle missing data; do not weaken a complete-table contract accidentally.
Closed keys and dynamic keys need different expectations
Record<Status,string> requires one label for every known status. Record<string,number> cannot promise that every possible runtime string has a value; a dynamic lookup may be undefined under the course settings.
Choose a closed key union for a complete table and a dynamic dictionary for genuinely open input. Do not widen a finite vocabulary to string just to silence a missing-key error: that removes useful coverage checks.
type Status = "planned" | "done";
const labels: Record<Status, string> = { planned: "Next", done: "Complete" };
console.log(labels.planned);
console.log(labels.done);Enable JavaScript to edit and type-check this example.
Check your prediction
Next
Complete Every valid Status has a label. Adding another status forces a deliberate table update, which is useful maintenance evidence rather than an inconvenience to bypass.
const totals: Record<string, number> = { Types: 0 };
console.log(totals["Types"] ?? 99);
console.log(totals["Missing"] ?? 99);
console.log(totals["Types"] || 99);Enable JavaScript to edit and type-check this example.
Check your prediction
0
99
99 Nullish fallback preserves a real zero. The || expression replaces it. A dictionary being numeric does not justify treating every absent or falsy lookup the same way.
Try the rule on new inputs
Add a new closed status and observe the missing-label error. For the dynamic dictionary, test an existing zero, an existing positive amount and a missing key. State which lookup needs a fallback.
Your turn
Records & Dynamic Keys: practise
Create a Record for weekday keys “Mon” | “Tue” | “Wed” with numeric minutes. Write dayMinutes(day) to look up a valid day. Print 20 for Tue. Include all three keys.
// Define Day, a complete minutes table, and a typed lookup function.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
A closed key union makes the complete table checkable.
Hint 2: a first step
Declare Day as a literal union and use it as the first argument to Record.
Show one solution
type Day = "Mon" | "Tue" | "Wed";
const minutes: Record<Day, number> = { Mon: 10, Tue: 20, Wed: 0 };
function dayMinutes(day: Day): number {
return minutes[day];
}
console.log(dayMinutes("Tue"));Enable JavaScript to edit and type-check this example.
20Explain the types at the function boundary and the runtime checks, if any. Compare your approach before copying this answer.
Change it and explain it
Remove Wed and inspect the error, then restore it. Change the table to Record<string, number> and explain why arbitrary lookups now need a missing-value policy. Do not “fix” a missing key by claiming that it can never be missing.
Completion is saved in this browser only. It does not measure mastery.