`Partial<T>` inner workings
Chapter 3: TypeScript for real-world applications
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).
We use cookies to collect statistics through Google Analytics.
Do not track
 
Allow cookies