Lesson 18 of 25
Mapped Types
Build a new object type by looping over an existing type's keys with in keyof, add or strip modifiers with +/-, and rename keys with key remapping.
Partial<T>, Readonly<T>, Pick<T, Keys> — every utility type from two
lessons ago that transforms an existing object type is built from the same
underlying mechanism: looping over a type's keys and describing what each
resulting property should look like. That mechanism is a mapped type,
and this lesson is about writing your own.
keyof: getting a type's keys, as a type
Before mapping over keys, you need a way to refer to them. keyof produces
a union of a type's property names, as string literal types:
interface User {
name: string;
age: number;
}
type UserKeys = keyof User; // "name" | "age"This isn't a runtime array — you can't .map() over it or log it directly.
It's a type, existing only for the compiler, in exactly the same way every
other type in this track does.
The basic mapped type
{ [K in Union]: ... } loops over every member of a union type (almost
always produced by keyof) and produces a property for each one:
type Identity<T> = {
[K in keyof T]: T[K];
};
type SameAsUser = Identity<User>; // { name: string; age: number } — identical to UserT[K] here is an indexed access type — "the type of property K on
T" — read it the same way you'd read user[key] at the value level, just
one level up, operating on types instead of values.
This particular mapped type does nothing new; it's the identity operation, included specifically because every useful mapped type starts from this exact shape and adds something to it.
Adding a modifier
Wrapping the property in ? or readonly inside the loop applies that
modifier to every resulting property:
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};These are, almost verbatim, the real definitions of Partial<T> and
Readonly<T> from TypeScript's own standard library — nothing about them
was ever a special compiler built-in.
Removing a modifier with -
Modifiers can be explicitly added (+, or nothing — the default) or removed
(-):
type MyRequired<T> = {
[K in keyof T]-?: T[K]; // strip optionality, making every property required
};
interface Settings {
theme?: string;
fontSize?: number;
}
type FullSettings = MyRequired<Settings>; // { theme: string; fontSize: number }This is the actual definition of Required<T> — a mapped type using -?
to remove optionality from every property, the mirror image of Partial<T>
adding it.
Filtering keys with a conditional inside the loop
Combining a mapped type with a conditional type (previous lesson) lets you select only some keys, rather than mapping over all of them:
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
email: string;
}
type StringFields = OnlyStrings<Mixed>; // { name: string; email: string }Remapping a key to never — as the age property does here, since it
fails the T[K] extends string check — removes that property from the
result entirely. This is the same as key remapping used above to also
rename keys, just used here to conditionally drop some of them instead.
Key remapping with as, briefly previewed
Beyond filtering, as inside a mapped type can transform the key itself
into a genuinely different name:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};This uses a template literal type to build a new key name out of an
existing one ("name" becomes "getName") — powerful enough, and distinct
enough as its own topic, that it gets the entire next lesson.
Try it yourself
What to remember
keyof Tproduces a union of T's property names as string literal types — a type, not a runtime value.{ [K in keyof T]: T[K] }is the identity mapped type; every built-in object utility type (Partial, Required, Readonly, Pick) is a small variation on this shape.+/-before?orreadonlyinside a mapped type explicitly adds or removes that modifier from every resulting property.- Combining
aswith a conditional inside the loop can filter out keys entirely (mapping tonever) or rename them — the subject of the next lesson.
Check yourself
4 questions · pass 3/4 to unlock Template Literal Types
1.What does
keyof Tproduce forinterface User { name: string; age: number }?2.What does
{ [K in keyof T]: T[K] }produce, for any object type T?3.What does the
-?modifier do in{ [K in keyof T]-?: T[K] }?4.What does
asdo in a mapped type, e.g.{ [K in keyof T asget${string & K}]: T[K] }?
4 left to answer