Mapped Type { [K in keyof T]: ... }
| Since: | TypeScript 2.1(2016) |
|---|
A feature that generates a new type by transforming the properties of an existing type. It can automate repetitive type definitions, and is used internally by built-in utility types such as Partial and Readonly.
Syntax
type TypeName = {
[K in keyof T]: NewType;
};
// Adding and removing modifiers
type TypeName = {
+readonly [K in keyof T]+?: NewType; // Adds readonly and ?.
-readonly [K in keyof T]-?: NewType; // Removes readonly and ?.
};
Syntax Reference
| Syntax | Description |
|---|---|
| [K in keyof T] | Iterates over all property keys of type T as K. This is the fundamental pattern of mapped types. |
| keyof T | Retrieves all property keys of type T as a union type. |
| [K in Union] | Iterates over members of a specific union type as keys. |
| +?: / -?: | Adds (+) or removes (-) the optional modifier (a qualifier such as readonly or ? that is attached to a property) from properties. |
| +readonly / -readonly | Adds (+) or removes (-) readonly from properties. |
| as clause (key remapping) | Remaps key names using the [K in keyof T as NewKey] syntax (TypeScript 4.1 and later). |
Sample Code
type User = {
id: number;
name: string;
email: string;
};
// Converts all properties to string.
type UserAsStrings = {
[K in keyof User]: string;
};
// Results in: { id: string; name: string; email: string; }
// Manual implementation of Partial<T>: makes all properties optional.
type MyPartial<T> = {
[K in keyof T]?: T[K]; // T[K] preserves the original property type.
};
type PartialUser = MyPartial<User>;
const partial: PartialUser = { name: "member_1" }; // id and email can be omitted.
console.log(partial.name); // Outputs "member_1".
// Manual implementation of Readonly<T>: makes all properties readonly.
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
const readonlyUser: MyReadonly<User> = { id: 1, name: "member_2", email: "member_2@wp-p.info" };
// readonlyUser.name = "member_1"; // Error: cannot modify a readonly property.
// Uses -?: to remove the optional modifier from all properties (implementation of Required<T>).
type MyRequired<T> = {
[K in keyof T]-?: T[K];
};
// Uses a union type as keys.
type Flags = "dark" | "sidebar" | "notifications";
type FlagMap = {
[K in Flags]: boolean;
};
// Results in: { dark: boolean; sidebar: boolean; notifications: boolean; }
const settings: FlagMap = { dark: true, sidebar: false, notifications: true };
console.log(settings.dark); // Outputs true.
// Remaps key names using an as clause (TypeScript 4.1 and later).
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// Results in: { getId: () => number; getName: () => string; getEmail: () => string; }
Running the above produces the following output:
npx ts-node ts_mapped_type.ts member_1 true
Overview
A mapped type uses the syntax { [K in keyof T]: ... } to generate a new type that transforms all properties of an existing type. TypeScript's built-in utility types — Partial<T>, Readonly<T>, Required<T>, and Pick<T, K> — are all implemented using mapped types.
Modifiers (+?: / -?: / +readonly / -readonly) let you add or remove the optional state and readonly on properties in the resulting type. Using -?: creates a type where all properties are required, similar to Required<T>.
Starting with TypeScript 4.1, you can also remap key names using an as clause. Combined with template literal types, you can automatically generate getter types such as getId() and getName().
If you find any errors or copyright issues, please contact us.