本文介绍: TypeScript映射对象类型是一种高级类型,允许您定义一种新类型,该类型是从现有类型派生的,并且对现有类型每个属性进行修改映射类型可以帮助您在编写TypeScript代码时减少代码的冗余,内置映射修饰符提供了一种方便的方式处理现有类型中的每个属性,并生成一个新类型。映射修饰符包括 Partial、Required、Readonly、Record、Pick、Omit 等。下面我们来看一些常见的应用场景

TypeScript映射对象类型是一种高级类型,允许您定义一种新类型,该类型是从现有类型派生的,并且对现有类型的每个属性进行修改。 映射类型可以帮助您在编写TypeScript代码时减少代码的冗余,并简化某些常见任务

1、映射对象类型声明

在TypeScript中,映射类型可以使用索引类型和条件类型一起创建以便一个类型中的每个属性映射到另一个类型。 映射类型的语法如下:

type NewType = { [P in keyof OldType]: NewValueType }

其中
OldType 是要映射的类型,
NewValueType 是要将每个属性映射到的新类型,
keyof 操作符用于获取现有类型的所有属性名称
in 关键字后面的方括号中,我们属性名称 Pkeyof OldType 进行匹配,并将其重新映射为新类型 NewValueType

2、映射对象类型解析

以下是一个简单示例,其中我们具有字符串值的现有类型的每个属性映射到具有数字值的新类型:

type OldType = { a: string, b: string, c: string };
type NewType = { [P in keyof OldType]: number };

// 新类型为 { a: number, b: number, c: number }

映射类型还支持循环类型,您可以使用 keyof 操作符和条件类型来定义复杂的映射类型。例如,以下示例一个具有字符串值的对象的所有属性映射到一个具有相应数组类型的新类型:

type ObjectWithStringValues = { [key: string]: string };
type ObjectWithArrayValues = { [P in keyof ObjectWithStringValues]: string[] };

// 新类型为 { [key: string]: string[] }

下面是它的详细运算步骤

首先,keyof OldType 获取旧类型 OldType 的所有属性名称,并返回一个联合类型。

然后in 关键字与方括号中的属性名称 P 进行匹配。在这里我们使用 P 来表示 OldType 中的每个属性名称

P 作为索引键,在方括号中出现,并且在冒号后面跟着新类型 NewValueType指定要将每个属性映射到的新类型。

最后,将所有属性名称 P 替换为新类型 NewValueType,并将它们组合在一起以形成新的映射类型。

3、映射对象类型应用

映射对象类型是 TypeScript 的一项强大功能可以让您更轻松地处理各种常见的编程任务。下面是一些映射对象类型的应用

3.1 Pick用法实现

如果您有一个对象类型,但想从中删除一些属性可以使用 Pick 和 TypeScript 中的映射类型来实现

在这个示例中,我们使用自定义Pick 实现来创建一个新类型 PersonInfo,该类型只包含 Person 接口中的 nameage 属性。最后,我们创建一个 person 对象,它只包含 PersonInfo指定的这两个属性。

interface Person {
  name: string;
  age: number;
  address: string;
  phone: string;
}

type PersonInfo = Pick<Person, 'name' | 'age'>;

const person: PersonInfo = {
  name: 'Alice',
  age: 30,
};

下面是一个简单的 TypeScript 实现 Pick 的示例

type Pick<SourceType, KeysType extends keyof SourceType> = {
  [KeyType in KeysType]: SourceType[KeyType];
};

在这个实现中,我们使用了两个泛型类型参数

3.2 Omit用法及实现

可以从一个类型中省略指定的属性。它可以非常方便地快速生成一个新的类型,去掉原来类型中的一些属性。

下面是一个示例,使用 Omit 实现一个可以从对象中省略指定属性的函数

const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit <T, K> => {
    const result = {...obj};
    keys.forEach((key) => {
        delete result[key];
    });
    return result;
}

interface Person {
  name: string;
  age: number;
  email: string;
}

const person: Person = {
  name: "John",
  age: 30,
  email: "john@example.com",
};

const result = omit(person, "email");

console.log(result); // { name: "John", age: 30 }

在上面的示例中,我们定义了一个 omit 函数,该函数接受一个对象和一些要省略的属性。我们使用 …keys 将要省略的属性作为可变参数传递给函数。

函数使用 forEach 方法遍历要省略的属性,然后使用 delete 运算符从对象中删除它们。最后,函数返回一个省略了指定属性的新对象。在这里,我们使用Omit<T, K>类型来省略 K 类型的属性。

下面是一个简单的 TypeScript 实现 Omit 的示例:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

在这个实现中,我们使用了 Pick Exclude 类型。

  • 首先,我们使用 Exclude<keyof T, K>获取所有不在 K 中的属性。
  • 然后,我们使用 Pick<T, ...>选取这些属性,从而生成一个新的类型,该类型省略了原类型中指定的属性。

需要注意的是,我们在这里使用了 Exclude 类型来排除 K 中的属性。在 TypeScript 中,Exclude可以从一个类型中排除另一个类型。在这里,我们使用 Exclude<keyof T, K> 来从 keyof T 中排除 K 类型的属性,从而获取所有不在 K 中的属性。由于 Pick<T, ...>选取指定的属性,因此我们得到了一个省略了指定属性的新类型。

3.3 Partial用法与实现

将一个对象类型的所有属性变为可选。这对于定义一些可能具有可选属性的接口或类型非常有用

下面是一个示例,使用 Partial 实现一个可以部分更新用户信息的函数:

interface User {
  name: string;
  age: number;
  email: string;
}

const updateUser = (user: User, updates: Partial<User>): User => {
  return { ...user, ...updates };
}

const user: User = { name: 'Alice', age: 30, email: 'alice@example.com' };

const updatedUser = updateUser(user, { name: 'Bob' });

在上面的示例中,我们定义了一个 User 接口,它有三个属性:nameageemail。然后我们定义了一个 updateUser 函数,该函数接受一个 User 对象和一个部分用户对象(即,它可以包含 User 对象的部分属性)。该函数返回一个新的 User 对象,其中包含原始 User 对象的所有属性和部分用户对象中的属性。

为了使 updateUser 函数能够接受部分用户对象,我们使用了 Partial<User> 类型。这个类型会将 User 对象中的所有属性变为可选。这意味着我们可以只传递 User 对象中的一部分属性到 updateUser 函数中,而不需要传递所有属性。

下面是一个简单的 TypeScript 实现 Partial 的示例。

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

在这个实现中,我们使用了映射对象类型,它会遍历 T 类型的所有属性,并将它们变为可选属性。由于 ? 符号将属性变为可选,因此我们只需要将属性的类型设置为原始属性的类型即可

需要注意的是,我们使用 keyof T获取 T 类型的所有属性名称。然后,我们使用 P in keyof T 循环遍历这些属性,并将它们变为可选属性。最后,我们使用 T[P] 获取原始属性的类型,并将其设置为可选属性的类型。

3.4 Required用法及实现

将一个对象类型中的所有属性变为必需的。这对于定义一些需要确保所有属性都被填充的接口或类型非常有用。

下面是一个示例,使用 Required 实现一个可以创建必填订单的函数:

interface Order {
  id?: number;
  customerId?: number;
  date?: Date;
  amount?: number;
}

const createOrder = (order: Required<Order>): void => {
  // ...
}

const order: Order = { customerId: 123, amount: 100 };
createOrder(order); // Error: Property 'id' is missing

在上面的示例中,我们定义了一个 Order 接口,它有四个属性:idcustomerIddate amount,其中所有属性都是可选的。然后我们定义了一个 createOrder 函数,该函数接受一个必填订单对象,并在函数内部进行一些操作

在函数调用中,我们只传递了 Order 对象中的一部分属性,即 customerId amount。由于 Order 对象的其他属性是可选的,因此 TypeScript 编译器报错提示缺少 id 属性。

为了将 Order 对象中的所有属性变为必需属性,我们可以使用 Required<Order> 类型。这个类型会将 Order 对象中的所有属性变为必需属性。这意味着我们必须在创建 Order 对象时填写所有属性。

下面是一个简单的 Typescript 实现 Required 的示例:

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

在这个实现中,我们使用了映射对象类型,它会遍历 T 类型的所有属性,并将它们变为必需属性。由于 – 符号将属性变为必需属性,因此我们只需要将属性的类型设置为原始属性的类型即可

需要注意的是,我们使用 keyof T 获取 T 类型的所有属性名称。然后,我们使用P in keyof T 循环遍历这些属性,并将它们变为必需属性。最后,我们使用 T[P] 获取原始属性的类型,并将其设置为必需属性的类型。

3.5 Exclude用法及实现

可以从一个类型中排除另一个类型。它可以非常方便地过滤掉某些不需要的类型。

下面是一个示例,使用 Exclude 实现一个可以从数组中排除指定类型的函数:

const excludeValues = <T, U extends T>(arr: T[], ...values: U[]): Exclude<T, U>[] => {
    return arr.filter((x) => !values.includes(x)) as Exclude<T, U>[];
}

const arr = [1, 2, 3, "foo", "bar", true];
const result = excludeValues(arr, "foo", true);

console.log(result); // [1, 2, 3, "bar"]

在上面的示例中,我们定义了一个 excludeValues 函数,该函数接受一个数组和一些要排除的值。我们使用 ...values 将要排除的值作为可变参数传递给函数。

函数使用 filter 方法过滤数组中包含在要排除的值中的元素,并返回一个排除了指定类型的新数组。在这里,我们使用 Exclude<T, U> 类型来排除 U 类型的元素

下面是一个简单的 Typescript 实现 Exclude 的示例:

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

在这个实现中,我们使用了条件类型,它会判断 T 类型是否可以分配给 U 类型。如果可以分配,则返回 never 类型,否则返回 T 类型。这个实现的含义是,如果 T 类型可以分配给 U 类型,则说明 T 类型是要排除的类型,因此我们将其映射为 never 类型;否则,T 类型不是要排除的类型,因此我们将其保留。

需要注意的是,我们使用 extends 关键字来判断 T 类型是否可以分配给 U 类型。如果可以分配,则说明 T 类型可以被排除,因此我们将其映射为 never 类型;否则,T 类型不能被排除,因此我们将其保留。由于 never 类型表示无法实例化的类型,因此它可以用于过滤掉不需要的类型。

3.6 Record用法及实现

将一组键映射到一组值上,并生成一个包含这些键值对的新类型。它的基本语法如下:

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

其中,K 是一组键的集合T对应的值的类型。下面是一些使用 Record 的示例和它们的实现。

示例 1:将字符串类型的键映射到数字类型的值。

type CountMap = Record<string, number>;

const countMap: CountMap = {
  "foo": 1,
  "bar": 2,
};

在上面的示例中,我们使用 Record<string, number>字符串类型的键映射到数字类型的值,并生成了一个 CountMap 类型。然后,我们可以使用 CountMap 类型来定义一个包字符串类型的键和数字类型的值的对象。

示例 2:将枚举类型的键映射到字符串类型的值

enum Color {
  Red,
  Green,
  Blue,
}

type ColorMap = Record<Color, string>;

const colorMap: ColorMap = {
  [Color.Red]: "#ff0000",
  [Color.Green]: "#00ff00",
  [Color.Blue]: "#0000ff",
};

在上面的示例中,我们使用 Record<Color, string>枚举类型 Color 的键映射到字符串类型的值,并生成了一个 ColorMap 类型。然后,我们可以使用 ColorMap 类型来定义一个包枚举类型的键和字符串类型的值的对象。

下面是一个简单的 Typescript 实现 Record 的示例:

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

// 示例
type CountMap = Record<string, number>;

const countMap: CountMap = {
  "foo": 1,
  "bar": 2,
};

在上面的实现中,我们使用了 TypeScript 中的 keyof 操作符和索引访问类型。keyof any 表示任何类型的所有属性名,而 [P in K]: T 则表示将所有 K 中的属性名映射为类型 T。因此,Record<K, T> 将一组键 K 映射到类型 T 上,并生成一个包含这些键值对的新类型。

3.7 Readonly用法及实现

给定类型的所有属性设置只读基本语法如下

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

其中,T 是要设置为只读的类型。下面是一些使用 Readonly 的示例和它们的实现。

示例 1:将对象的所有属性设置为只读。

interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;

const person: ReadonlyPerson = {
  name: "Alice",
  age: 30,
};

person.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.
person.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.

在上面的示例中,我们使用 Readonly<Person>Person 接口的所有属性设置为只读,并生成了一个 ReadonlyPerson 类型。然后,我们定义了一个包ReadonlyPerson 类型的对象 person,并尝试修改它的属性,但是会报错,因为属性是只读的。

示例 2:将对象的部分属性设置为只读。

interface Person {
  name: string;
  age: number;
  address: {
    city: string;
    street: string;
  };
}

type ReadonlyPerson = {
  readonly name: string;
  age: number;
  readonly address: {
    readonly city: string;
    readonly street: string;
  };
};

const person: ReadonlyPerson = {
  name: "Alice",
  age: 30,
  address: {
    city: "Beijing",
    street: "Chaoyang Road",
  },
};

person.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.
person.address.city = "Shanghai"; // Error: Cannot assign to 'city' because it is a read-only property.
person.address.street = "Nanjing Road"; // Error: Cannot assign to 'street' because it is a read-only property.

在上面的示例中,我们将 Person 接口nameaddress 属性设置为只读,并生成了一个 ReadonlyPerson 类型。然后,我们定义了一个包含 ReadonlyPerson 类型的对象 person,并尝试修改它的属性,但是只读属性是不能修改的,会报错。

下面是一个简单的 Typescript 实现 Readonly 的示例:

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

// 示例
interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;

const person: ReadonlyPerson = {
  name: "Alice",
  age: 30,
};

在上面的实现中,我们使用了 TypeScript 中的 keyof 操作符和索引访问类型。keyof T 表示 T 的所有属性名,而 [P in keyof T]: T[P] 则表示将所有属性名 P 映射为类型 T[P],同时添加readonly 修饰符,将所有属性设置。

目前这些是本人在工作中常用的一些内置类型,欢迎其他小伙伴的指点与建议,本人会第一时间完善,谢谢各位~

原文地址:https://blog.csdn.net/qq_39307572/article/details/129344314

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_14029.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注