Chapters - Table of contents →
Partial<T> return the T type with all its properties optional, but how does it achieve it?
Let's take a look at Partial<T> definition:
1type Partial<T> = {
2 [P in keyof T]?: T[P]
3}
4
As we already saw, type Partial<T> describes our Partial type as a generic type with a unique mandatory type argument T.
Let's now take a closer look at the [P in keyof T]?: T[P] part.
Index signatures
We saw in Chapter 2 that interfaces are closed by default:
1interface ButtonProps {
2 name: string,
3 type: 'primary' | 'secondary',
4 size: 'small' | 'medium' | 'large',
5 label: string,
6}
7
8const buttonProps: ButtonProps = {
9 name: 'save',
10 label: 'Save',
11 size: 'medium',
12 type: 'primary',
13}
14
15// ERROR: "Property 'myCustomProp'
16// does not exist on type 'ButtonProps'.ts(2339)"
17buttonProps.myCustomProp = { custom: 'data' }
18
However, we can make an interface "open" by specifying an index signature, as follows:
1interface ButtonProps {
2 name: string,
3 type: 'primary' | 'secondary',
4 size: 'small' | 'medium' | 'large',
5 label: string,
6 // index signature
7 [k: string]: string
8}
9
10
11const buttonProps: ButtonProps = {
12 name: 'save',
13 label: 'Save',
14 size: 'medium',
15 type: 'primary',
16 classNames: ['mt-50', 'mb-50']
17}
18
19// now works!
20buttonProps.myCustomProp = { custom: 'data' }
21
Let's take a closer look to the index signature:
1interface ButtonProps {
2 name: string,
3 // ...
4
5 // index signature
6 [k: string]: string
7}
8
- brackets are surrounding the type of the key, here k
- the key type [k: string] is suffixed with a value type: string
In short, { [k: string]: string } describes:
An object that can accept any property with a key of type string and value of type string
Here are some more examples:
1type User = {
2 id: string
3 [k: string]: string | number | boolean
4}
5// User describes an object with:
6// - a mandatory id property of type string
7// - any property with a key of type string and a value of type: boolean, string, or number
8
9interface Prices {
10 base: number
11 [k: string]: number
12}
13// Prices describes an object with:
14// - a mandatory base property of type number
15// - any property with a key of type string and a value of type number
16
The Mapped Type syntax
With Generic types, the index signature syntax allows to iterate over a given type argument properties, as it follows:
1// Given a type `Type`, returns all its properties as boolean
2type ToBoolean<Type> = {
3 [Property in keyof Type]: boolean
4}
5// `ToBoolean<Type>` iterates over all "values" of `Property`
6// which are all keys of `Type` (`Property in keyof Type`)
7type UserConfig = ToBoolean<{ acceptedPolicy: string, signedIn: string }>
8// UserConfig type is
9// {
10// acceptedPolicy: boolean
11// signedIn: boolean
12// }
13
Coming back to our Partial<T> definition:
1type Partial<T> = {
2 [P in keyof T]?: T[P]
3}
4
5// can also be written
6
7type Partial<Type> = {
8 [Property in keyof Type]?: Type[Property]
9}
10
What does the T[P]syntax mean?
Applying the [] on a type is called Lookup Types or also Indexed Access Types.
Here are some usage examples:
interface Person { age: number; name: string; alive: boolean };
type Age = Person["age"];
Age is of type number
interface Dropdown { name: string; popperConfig: { height: number } }
type PopperConfig = Dropdown["popperConfig"]
PopperConfig is of type { height: number }
Let's see a step by step overview of Partial<T> applied to our User type:
1interface User {
2 id: string,
3 firstName: string,
4 lastName: string,
5}
6
7type PartialUser = Partial<User>
8
9// results in the following steps with `T` = `User`:
10
11// 1. first property is `id`
12type Partial<T> = {
13 // `P` is "id", `T[P]` is `string`
14 [P in keyof T]?: T[P]
15}
16// for now, `PartialUser` is `{ id?: string }`
17
18// 2. second property is `firstName`
19type Partial<T> = {
20 // `P` is "firstName", `T[P]` is `string`
21 [P in keyof T]?: T[P]
22}
23// for now, `PartialUser` is `{ id?: string; firstName?: string }`
24
25// 3. last property is `lastName`
26type Partial<T> = {
27 // `P` is "lastName", `T[P]` is `string`
28 [P in keyof T]?: T[P]
29}
30// the final `PartialUser` type is `{ id?: string; firstName?: string; lastName? string }`
31
32
We can describe the Partial<Type> mapped type as:
Partial<Type>, given a type Type, iterates over all the properties of Type to add the ? modifier (optional).