Mapped types and key remapping: transforming every property at once
Build new object types from old ones with { [K in keyof T]: ... }, add or remove readonly and ?, rename keys with as, and drop keys by mapping them to never.
Comes up in: Coding
Learn#
Leena has a User type and needs three more: an editable draft, a version where everything is optional for PATCH requests, and getter methods for a class. Writing them by hand means four types to keep in sync.
A mapped type loops over keys and builds a new property for each: { [K in keyof T]: T[K] } copies T. Change the right side, and you change every property at once.
Key remapping with as renames keys, and mapping a key to never drops it:
type User = { readonly id: number; name: string; email?: string };
type Draft<T> = { -readonly [K in keyof T]-?: T[K] };
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
type OnlyStrings<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K] };
const draft: Draft<User> = { id: 1, name: "Asha" };
const getters: Getters<User> = { getId: () => 1, getName: () => "Asha", getEmail: () => 5 };
const strings: OnlyStrings<User> = { name: "Asha", id: 1 };example.ts(7,7): error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'Draft<User>'.
example.ts(8,73): error TS2322: Type '() => number' is not assignable to type '() => string | undefined'.
Type 'number' is not assignable to type 'string'.
example.ts(9,52): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'OnlyStrings<User>'.-? made email required, the getters kept each value’s type, and id was filtered out.
State
- K in "id" | "name" | "email"
keyof User gives the keys to loop over: "id" | "name" | "email".
Read the steps as text
- keyof User gives the keys to loop over: "id" | "name" | "email".
- K = "id": as renames it to getId, and the value becomes () => number.
- K = "name" and K = "email" go the same way.
- A key renamed to never is dropped. That is how OnlyStrings removes id.
Predict#
Predict first
type Copy<T> = { [K in keyof T]: T[K] }. Is Copy<{ readonly id: number }>['id'] still readonly?
A mapped type over keyof T is homomorphic: it keeps each property’s readonly and ? unless you add -readonly or -?. People expect a plain copy to lose them because nothing in the syntax mentions modifiers.
Interview angle#
Say it like this — about 30 seconds
“A mapped type loops over a set of keys, usually keyof T, and builds a property for each, like a map over an object type. Readonly maps every key K of T to a readonly copy of T[K], and Partial adds a question mark. Prefixing with minus removes a modifier. Since TypeScript 4.1, as renames keys, often with template literal types, and renaming a key to never removes it. Mapping over keyof T keeps the original modifiers.”
Follow-up Implement Readonly<T>.
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };Follow-up How do you remove optional from every property?
-?: { [K in keyof T]-?: T[K] }. That is how Required works.Follow-up How do you keep only function properties?
as T[K] extends (...a: any[]) => any ? K : never.Practice#
Guided
Write five mapped types without using the built-in utility types.
Done when
MyReadonly,MyPartialandMutablechange only the modifiers.Getters<{ name: string }>is exactly{ getName: () => string }.PickByValue<T, string>keeps only string properties.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Write the mapped type that copies T.
{ [K in keyof T]: T[K] }
How do you remove readonly in a mapped type?
-readonly before the key: { -readonly [K in keyof T]: T[K] }
How do you drop a key in a mapped type?
Remap it to never with as.
What is a homomorphic mapped type?
One that maps over keyof T and keeps T's modifiers.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
readonly or ? to put a modifier on every property; prefix - to remove it, or + to be explicit. Partial, Required and Readonly are exactly these three mapped types.Check yourself: Write Partial<T>.
{ [K in keyof T]?: T[K] }
How it works underneathSenior rounds
as takes any type that becomes the new key. Keys can be strings, numbers or symbols, so template literals need string & K Source: TypeScript Handbook: Mapped types.
Check yourself: Why does as need string & K in a template literal?
Keys may be numbers or symbols; string & K keeps only string keys, which a template can use.
Edge cases and trapsWhere people slip
never removes it.keyof T, keeps T’s modifiers, and on arrays and tuples it maps the elements and returns an array or tuple.Check yourself: What does a homomorphic mapped type do when T is an array?
It maps the elements and returns an array or tuple type.
In productionStaff-level answers
Partial, Required, Readonly, Pick and Record are mapped types Source: TypeScript Handbook: Utility types. Use them for form state, API patch payloads and event-handler maps, and derive from one source type.
Check yourself: Which built-in types are mapped types?
Partial, Required, Readonly, Pick and Record.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is a mapped type? Implement
Readonly<T>.