Understand mapped types in typescript

Time:2022-7-23

This article will introduce the mapped types in typescript in detail. After reading this article, you will learn the following knowledge points:

  • The relationship between mapping in mathematics and mapping type in TS;
  • Application of mapping type in TS;
  • Application of mapping type modifier in TS;

Next, we will start with “mapping in mathematics”.

The typescript version used in this article is v4.6.2

If you are not familiar with typescript, you can see the following materials:

  1. A rare TS Learning Guide (1.8W words)
  2. Great introduction to typescript

1、 What is mapping?

When learning typescript type system, try to learn by analogy with sets in mathematics, such as joint types in typescript, union sets in mathematics, etc.

In mathematics, mapping refers toThe corresponding relationship between elements in the set of two elements, such as the following figure:

Understand mapped types in typescript
(source:https://baike.baidu.com/item/%E6%98%A0%E5%B0%84/20402621

The mapping can be understood as a function, as shown in the above figure. When we need to convert the elements of set a into those of set B, we can use thefFunction to map, for example, the elements of set a1Corresponding to elements in set B2
In this way, the mapping process can be well implementedmultiplexing

2、 What is the mapping type in typescript?

1. Concept introduction

The mapping type in typescript is similar to that in mathematics. It can convert the elements of a set into the elements of a new set, butTypescript mapping type is to map one type to another

In our actual development, we often need to convert all attributes of a type into optional types. At this time, you can directly use thePartialTool type:

type User = {
  name: string;
  location: string;
  age: number;
}

type User2 = Partial<User>;
/*
  Type of user2:
  
  type User2 = {
      name?: string | undefined;
      location?: string | undefined;
      age?: number | undefined;
  }
*/

In this way, we will achieveUserType maps toUser2Type, and willUserAll properties in the type are converted to optional types.

Understand mapped types in typescript

2. Implementation method

The syntax of typescript mapping types is as follows:

type TypeName<Type> = {
  [Property in keyof Type]: boolean;
};

Since we can passPartialTool types are very simple to implementConverts all properties of the specified type to optional typesWhat is the content principle?

In the editor, we can hover the mouse overPartialAbove the name, you can see the editor prompt as follows:

Understand mapped types in typescript

Disassemble each part:

  • type Partial<T>: define a type aliasPartialAnd genericsT
  • keyof T: PasskeyofOperator get genericsTAll inkey, return a union type (if you don’t know what a union type is, it can be understood as an array);
type User = {
  name: string;
  location: string;
  age: number;
}

type KeyOfUser = keyof User; // "name" | "location" | "age"
  • in: similar to JSfor...inMediumin, used to traverse the public attribute name of the target type;
  • T[P]: is an index access type (also known as lookup type), get genericTinPType, similar to the way of accessing objects in JS;
  • ?:Set the type value as an optional type;
  • { [P in keyof T] ?: T[P] | undefined}: traversalkeyof TThe returned union type, and define the union type withPVariable reception, and the value returned by each iteration is of optional typeT[P]

That’s itPartialTool type, this operation method is very important, and it is an important foundation for typescript type gymnastics later.

About the practice of type gymnastics, you can see this article if you are interested:
“How many of these 30 TS exercises can you answer correctly?”https://juejin.cn/post/7009046640308781063

3、 Application of mapping type

Typescript mapping types are often used to reuse some operations on types, such as the 21 tool types currently supported by typescript. We define some commonly used type operations as these tool types to facilitate developers to reuse these types.

All supported tool types can be found in the following official documents:
https://www.typescriptlang.org/docs/handbook/utility-types.html

Next, let’s pick a few common tool types and see how mapping types are used in their implementation.

In the process of learning typescript, it is recommended to practice and learn more in the official Playground:
https://www.typescriptlang.org/zh/play

1. Required required attribute

be used forSet all properties of the type as required

The implementation is as follows:

type Required<T> = {
    [P in keyof T]-?: T[P];
};

Usage:

type User = {
  name?: string;
  location?: string;
  age?: number;
}

type User2 = Required<User>;
/*
  type User2 = {
      name: string;
      location: string;
      age: number;
  }
*/

const user: User2 = {
  name: 'pingan8787',
  age: 18
}
/*
  report errors:
  Property 'location' is missing in type '{ name: string; age: number; }'
  but required in type 'Required<User>'.
*/

This way-?Symbols can be temporarily understood as “converting optional attributes to mandatory attributes”, and these symbols will be described in detail in the next section.

2. Readonly read-only attribute

be used forSet the type of all properties to read-only type, that is, the type cannot be reassigned.

The implementation is as follows:

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
}

Usage:

type User = {
  name?: string;
  location?: string;
  age?: number;
}

type User2 = Readonly<User>;
/*
  type User2 = {
      readonly name?: string | undefined;
      readonly location?: string | undefined;
      readonly age?: number | undefined;
  }
*/

const user: User2 = {
  name: 'pingan8787',
  age: 18
}

user.age = 20;
/*
  report errors:
  Cannot assign to 'age' because it is a read-only property.
*/

3. Pick the specified attribute

be used forSelect the specified property from the specified type and return

The implementation is as follows:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}

Use the following:

type User = {
  name?: string;
  location?: string;
  age?: number;
}

type User2 = Pick<User, 'name' | 'age'>;
/*
  type User2 = {
      name?: string | undefined;
      age?: number | undefined;
  }
*/

const user1: User2 = {
  name: 'pingan8787',
  age: 18
}

const user2: User2 = {
  name: 'pingan8787',
  Location:'xiamen', // an error is reported
  age: 18
}
/*
  report errors
  Type '{ name: string; location: string; age: number; }' is not assignable to type 'User2'.
  Object literal may only specify known properties, and 'location' does not exist in type 'User2'.
*/

4. Omit ignores specified attributes

Similar toPickThe tool type is opposite, and it canIgnore the specified attribute from the specified typeAnd return.

The implementation is as follows:

type Omit<T, K extends string | number | symbol> = {
  [P in Exclude<keyof T, K>]: T[P];
}

Usage:

type User = {
  name?: string;
  location?: string;
  age?: number;
}

type User2 = Omit<User, 'name' | 'age'>;
/*
  type User2 = {
      location?: string | undefined;
  }
*/

const user1: User2 = {
  location: 'xiamen',
}

const user2: User2 = {
  Name:'pingan8787', // an error is reported
  location: 'xiamen'
}
/*
  report errors:
  Type '{ name: string; location: string; }' is not assignable to type 'User2'.
  Object literal may only specify known properties, and 'name' does not exist in type 'User2'.
*/

5. Exclude exclude the specified type from the union type

be used forExclude the specified type from the specified union type

The implementation is as follows:

type Exclude<T, U> = T extends U ? never : T;

Usage:

type User = {
  name?: string;
  location?: string;
  age?: number;
}

type User2 = Exclude<keyof User, 'name'>;
/*
  type User2 = "location" | "age"
*/

const user1: User2 = 'age';
const user2: User2 = 'location';
const user3: User2 = 'name';  //  report errors
/*
  report errors:
  Type '"name"' is not assignable to type 'User2'.
*/

4、 Application of mapping modifier

When customizing mapping types, we can use two modifiers of mapping types to meet our requirements:

  • readonlyModifier: set the specified attribute toRead only type
  • ?Modifier: set the specified attribute tooptional type

Previously introducedReadonlyandPartialTool types have been used:

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
}

type Partial<T> = {
  [P in keyof T]?: T[P] | undefined;
}

Of course, you can also operate on modifiers:

  • +Add a modifier (used by default);
  • -Delete the modifier;

For example:

type Required<T> = {
    [P in keyof T]-?:  T[P]; //  Pass - delete? Modifier 
};

It can also be used on the front:

type NoReadonly<T> = {
  -readonly [P in keyof T]: T[P]; //  Pass - remove the readonly modifier
}

5、 Summary

This paper takes mapping in mathematics as a starting point, introduces typescript mapped type in detail, and introduces the application of mapped type and modifier.

When learning typescript type system, try to learn by analogy with sets in mathematics, such as joint types in typescript, union sets in mathematics, etc.

Learning mapping type well is a very important foundation for doing type gymnastics next~~

reference material

  1. Typescript document – mapping type:https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
  2. Typescript tool type:https://www.typescriptlang.org/docs/handbook/utility-types.html