keyof: the property names as a type
keyof T is a union of T's property names as literal types:
interface Point { x: number; y: number; } // keyof Point is "x" | "y" function getCoord(p: Point, key: keyof Point): number { return p[key]; }
getCoord(p, "x") compiles, getCoord(p, "z") does not. In plain JavaScript, p[key] with a misspelled key silently returns undefined. With keyof, property access by name is checked like everything else. You already saw Pick<User, "id" | "name"> in the last lesson, that K is constrained with keyof behind the scenes.
Code exercise · typescript
Run it, then call getCoord({ x: 3, y: 7 }, "z") and read the error listing the allowed keys.
Record: a typed lookup table
Record<K, V> builds an object type with keys K and values V. Pair it with a union of literals for an exhaustive table:
type Fruit = "apple" | "banana" | "cherry"; const stock: Record<Fruit, number> = { apple: 4, banana: 0, cherry: 12, };
Two guarantees at once: every fruit must appear (forget cherry and it will not compile), and no stray keys sneak in. Compare the JavaScript habit of plain object maps where a typo creates a new key instead of an error. Record<string, number> is also legal when the key set is open-ended.
Code exercise · typescript
Record<string, number> with open-ended keys, used as a tally. Note the honest wrinkle: the type says counts[w] is a number, but a key seen for the first time is undefined at runtime, so ?? 0 (lesson 4-4) supplies the starting value. Run it, then print counts["fox"] to see what a never-seen key gives.
Code exercise · typescript
Your turn. Define type Day = "mon" | "tue" | "wed". Create steps: Record<Day, number> with mon 4200, tue 8000, wed 5600. Add the three values into total and print "total steps: " + total.
Quiz
You declare const scores: Record<"alice" | "bob", number> = { alice: 3 }. What happens?