Skip to content

Commit 210e087

Browse files
committed
initial commit
1 parent 0af42a1 commit 210e087

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: Mapped Types
3+
layout: docs
4+
permalink: /docs/handbook/2/mapped-types.html
5+
oneline: "Generating types by re-using an existing type."
6+
---
7+
8+
When you don't want to repeat yourself, sometimes a type needs to be based on another type.
9+
10+
Mapped types build on the syntax for index signatures, which are used to declare the types of properties which have not been declared ahead of time:
11+
12+
```ts twoslash
13+
type Horse = {};
14+
// ---cut---
15+
type OnlyBoolsAndHorses = {
16+
[key: string]: boolean | Horse;
17+
};
18+
19+
const conforms: OnlyBoolsAndHorses = {
20+
del: true,
21+
rodney: false,
22+
};
23+
```
24+
25+
A mapped type is a generic type which uses a union of `PropertyKey`s (frequently created [via a `keyof`](/docs/handbook/2/indexed-access-types.html)) to iterate through keys to create a type:
26+
27+
```ts twoslash
28+
type OptionsFlags<Type> = {
29+
[Property in keyof Type]: boolean;
30+
};
31+
```
32+
33+
In this example, `OptionsFlags` will take all the properties from the type `Type` and change their values to be a boolean.
34+
35+
```ts twoslash
36+
type OptionsFlags<Type> = {
37+
[Property in keyof Type]: boolean;
38+
};
39+
// ---cut---
40+
type FeatureFlags = {
41+
darkMode: () => void;
42+
newUserProfile: () => void;
43+
};
44+
45+
type FeatureOptions = OptionsFlags<FeatureFlags>;
46+
// ^?
47+
```
48+
49+
### Mapping Modifiers
50+
51+
There are two additional modifiers which can be applied during mapping: `readonly` and `?` which affect mutability and optionality respectively.
52+
53+
You can remove or add these modifiers by prefixing with `-` or `+`. If you don't add a prefix, then `+` is assumed.
54+
55+
```ts twoslash
56+
// Removes 'readonly' attributes from a type's properties
57+
type CreateMutable<Type> = {
58+
-readonly [Property in keyof Type]: Type[Property];
59+
};
60+
61+
type LockedAccount = {
62+
readonly id: string;
63+
readonly name: string;
64+
};
65+
66+
type UnlockedAccount = CreateMutable<LockedAccount>;
67+
// ^?
68+
```
69+
70+
```ts twoslash
71+
// Removes 'optional' attributes from a type's properties
72+
type Concrete<Type> = {
73+
[Property in keyof Type]-?: Type[Property];
74+
};
75+
76+
type MaybeUser = {
77+
id: string;
78+
name?: string;
79+
age?: number;
80+
};
81+
82+
type User = Concrete<MaybeUser>;
83+
// ^?
84+
```
85+
86+
## Key Remapping via `as`
87+
88+
In TypeScript 4.1 and onwards, you can re-map keys in mapped types with an `as` clause in a mapped type:
89+
90+
```ts
91+
type MappedTypeWithNewProperties<Type> = {
92+
[Properties in keyof Type as NewKeyType]: Type[Properties]
93+
}
94+
```
95+
96+
You can leverage features like [template literal types](/docs/handbook/2/template-literal-types.html) to create new property names from prior ones:
97+
98+
```ts twoslash
99+
type Getters<Type> = {
100+
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
101+
};
102+
103+
interface Person {
104+
name: string;
105+
age: number;
106+
location: string;
107+
}
108+
109+
type LazyPerson = Getters<Person>;
110+
// ^?
111+
```
112+
113+
You can filter out keys by producing `never` via a conditional type:
114+
115+
```ts twoslash
116+
// Remove the 'kind' property
117+
type RemoveKindField<Type> = {
118+
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
119+
};
120+
121+
interface Circle {
122+
kind: "circle";
123+
radius: number;
124+
}
125+
126+
type KindlessCircle = RemoveKindField<Circle>;
127+
// ^?
128+
```
129+
130+
You can map over arbitrary unions, not just unions of `string | number | symbol`, but unions of any type:
131+
132+
```ts twoslash
133+
type EventConfig<Events extends { kind: string }> = {
134+
[E in Events as E["kind"]]: (event: E) => void;
135+
}
136+
137+
type SquareEvent = { kind: "square", x: number, y: number };
138+
type CircleEvent = { kind: "circle", radius: number };
139+
140+
type Config = EventConfig<SquareEvent | CircleEvent>
141+
// ^?
142+
```
143+
144+
### Further Exploration
145+
146+
Mapped types work well with other features in this type manipulation section, for example here is [a mapped type using a conditional type](/docs/handbook/2/conditional-types.html) which returns either a `true` or `false` depending on whether an object has the property `pii` set to the literal `true`:
147+
148+
```ts twoslash
149+
type ExtractPII<Type> = {
150+
[Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
151+
};
152+
153+
type DBFields = {
154+
id: { format: "incrementing" };
155+
name: { type: string; pii: true };
156+
};
157+
158+
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
159+
// ^?
160+
```

0 commit comments

Comments
 (0)