TypeScript映射对象类型是一种高级类型,允许您定义一种新类型,该类型是从现有类型派生的,并且对现有类型的每个属性进行修改。 映射类型可以帮助您在编写TypeScript代码时减少代码的冗余,并简化某些常见任务。
1、映射对象类型声明
在TypeScript中,映射类型可以使用索引类型和条件类型一起创建,以便将一个类型中的每个属性映射到另一个类型。 映射类型的语法如下:
type NewType = { [P in keyof OldType]: NewValueType }
其中
OldType
是要映射的类型,
NewValueType
是要将每个属性映射到的新类型,
keyof
操作符用于获取现有类型的所有属性名称,
在 in
关键字后面的方括号中,我们将属性名称 P
与 keyof 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
接口中的 name
和 age
属性。最后,我们创建一个 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];
};
SourceType
表示要选择属性的源类型。KeysType
是一个字符串字面量类型的联合类型,它表示要从源类型中选择的属性名称。- 接下来,我们使用TypeScript中的
in
关键字来迭代KeysType
中的每个属性名称,并使用[ ]
访问运算符从SourceType
中提取出相应的属性类型。 - 最后,我们使用 TypeScript 中的映射类型语法来创建一个新的类型,该类型只包含
KeysType
中指定的属性。
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
类型来排除 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
接口,它有三个属性:name
、age
和 email
。然后我们定义了一个 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
接口,它有四个属性:id
、customerId
、date
和 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
的示例和它们的实现。
type CountMap = Record<string, number>;
const countMap: CountMap = {
"foo": 1,
"bar": 2,
};
在上面的示例中,我们使用 Record<string, number>
将字符串类型的键映射到数字类型的值,并生成了一个 CountMap
类型。然后,我们可以使用 CountMap
类型来定义一个包含字符串类型的键和数字类型的值的对象。
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
接口的 name
和 address
属性设置为只读,并生成了一个 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进行投诉反馈,一经查实,立即删除!