`{ [K in keyof T]: ... }` — Transform Every Property
Mapped Types
Mapped types create a new type by walking another type's keys. The foundation of `Partial`, `Readonly`, and the rest of the utility types.
What you'll learn
- Read mapped-type syntax
- Modify properties (optional, readonly)
- Add or remove modifiers
A mapped type builds a new type by walking each key of another type and transforming the value.
The Syntax
type Stringify<T> = {
[K in keyof T]: string;
};
type User = { name: string; age: number };
type StringyUser = Stringify<User>;
// { name: string; age: string } Read it as: “For each K in keyof T, set the property type to
string.”
Make All Optional
type Optional<T> = {
[K in keyof T]?: T[K];
};
type PartialUser = Optional<User>;
// { name?: string; age?: number } The ?: adds the optional modifier. This is exactly what the
built-in Partial<T> does.
Make All Readonly
type ReadOnly<T> = {
readonly [K in keyof T]: T[K];
}; The built-in Readonly<T>.
Remove Modifiers — -? and -readonly
type Required<T> = {
[K in keyof T]-?: T[K]; // strip optional
};
type Mutable<T> = {
-readonly [K in keyof T]: T[K]; // strip readonly
}; -? strips ?:. -readonly strips readonly. Often used inside
larger transformations.
Transform the Value Type
type Promisify<T> = {
[K in keyof T]: T[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: T[K];
}; Wraps every method’s return in a Promise. Uses conditional and infer (covered shortly).
Key Remapping with as
You can rename keys as you map:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number } The Capitalize<...> is a built-in string transformer; we cover
template literal types next lesson.
Filtering Keys With never
To exclude keys, map them to never and then filter:
type StringKeys<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type Mixed = { name: string; age: number; tags: string };
type Strings = StringKeys<Mixed>;
// { name: string; tags: string } Mapped types + conditionals + as rename = serious power.
Up Next
Template literal types — for string-level type manipulation.
Template Literal Types →