C.W.K.
Stream
Lesson 03 of 05 · published

`Record<K, V>` and `Readonly<T>`

~8 min · utility-types, record, readonly, dictionaries

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Record describes dictionaries. Readonly describes immutability. Both are everyday utilities."

Record<K, V> — dictionary type

Record<K, V> creates an object type where keys are of type K and values are of type V. Record<string, number> is a dictionary mapping any string to a number. Record<'a' | 'b' | 'c', User> is an object that must have exactly the keys 'a', 'b', 'c', each mapping to a User.

Record is the canonical type for dictionary-shaped data — phone books, lookup tables, configuration maps. When K is a literal union, Record acts like an enum-keyed map; when K is string, it's an open dictionary.

Readonly<T> — make every field readonly

Readonly<User> takes User and produces a type where every field has the readonly modifier. Useful for function parameters where you want to signal 'I won't mutate this' and for freezing configuration shapes.

Like Partial, it's shallow — the readonly applies to top-level fields only. For deep readonly, you'd write a recursive mapped type or use a library like ts-toolbelt's DeepReadonly.

Record + literal-union keys is how you describe 'an object that must have exactly these keys.' Combined with Pick, you can express almost any shape relationship using utility types alone.

Code

Record and Readonly — dictionaries and immutability·typescript
// Record — dictionaries.
type PhoneBook = Record<string, string>;       // any string key → string
const pb: PhoneBook = { pippa: '555-0001', dad: '555-0002' };

// Record with a literal union — keys are exactly those.
type RGB = Record<'r' | 'g' | 'b', number>;
const color: RGB = { r: 255, g: 0, b: 128 };   // all three required
// const bad: RGB = { r: 255 };                 // ❌ missing g, b

// Readonly — every field readonly.
type FrozenUser = Readonly<{ id: number; name: string }>;
const u: FrozenUser = { id: 1, name: 'Pippa' };
u.name = 'Other';        // ❌ Cannot assign to 'name' because it is a read-only property

// Common combination — a frozen dictionary.
type FrozenMap<V> = Readonly<Record<string, V>>;

External links

Exercise

Build a ColorPalette type using Record where keys are 'primary' | 'secondary' | 'accent' and values are hex color strings (#RRGGBB). Then use Readonly to make the palette immutable. Try to add a fourth key — what does the compiler say?
Hint
Record with a literal union enforces exactly those keys. Adding 'tertiary' would fail because it's not in the union — Record-from-literal-union has the exhaustiveness property baked in.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.