TypeScript是一种强类型的编程语言,它在JavaScript的基础上添加了静态类型检查。除了基本的类型系统外,TypeScript还提供了许多高级类型特性,其中之一就是Mapped Types(映射类型)。Mapped Types是一种强大的工具,可以根据现有类型创建新的类型。
Mapped Types
Mapped Types
的威力在于它们允许
我们对现有类型
进行转换
和修改
,从而创建出更具表现力和灵活性的类型。通过使用Mapped Types,我们可以轻松地进行属性的重命名、添加或删除属性、将属性设置为可选或只读,甚至可以根据现有属性的类型创建新的属性。
一个常见的用例是将现有类型的所有属性设置为只读。通过使用Readonly Mapped Typ
e,我们可以轻松地实现这一目标。例如,假设我们有一个名为Person
的接口,其中包含name
和age
属性。我们可以使用Readonly Mapped Type
将Person接口中的所有属性设置为只读
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
const person: ReadonlyPerson = {
name: "John",
age: 30
};
person.name = "Jane"; // Error: Cannot assign to 'name' because it is a read-only property.
除了Readonly
Mapped Type外,TypeScript还提供了许多其他的Mapped Types,如Partial
、Required
、Pick
等。这些Mapped Types可以根据我们的需求对现有类型进行灵活的转换和修改。
另一个强大的特性是使用Mapped Types进行属性的重命名。通过使用Record
Mapped Type,我们可以将一个类型中的属性重命名为另一个类型中的属性。例如,假设我们有一个名为User
的接口,其中包含username
和email
属性。我们可以使用Record
Mapped Type将User
接口中的username
属性重命名为name
:
interface User {
username: string;
email: string;
}
type RenamedUser = Record<"name", User["username"]> & Omit<User, "username">;
const user: RenamedUser = {
name: "John",
email: "john@example.com"
};
console.log(user.name); // Output: "John"
console.log(user.email); // Output: "john@example.com"
总结:
本文介绍了TypeScript中的Mapped Types,探索了它们的威力和实用性。Mapped Types允许我们根据现有类型创建新的类型,并进行属性的转换和修改。通过使用Mapped Types,我们可以轻松地实现属性的重命名、添加或删除属性、将属性设置为只读或可选等操作。掌握Mapped Types将使我们的代码更具表现力和灵活性,提高代码的可维护性和可扩展性。
衍生知识点
当涉及到Mapped Types时,其实还有其他较多的类似知识点,我们可以稍微提及一下。
- 条件类型(Conditional Types):条件类型是TypeScript中另一个强大的类型工具,它允许我们根据条件选择不同的类型。条件类型通常与Mapped Types结合使用,以根据某些条件转换或修改类型。通过使用条件类型,我们可以实现更复杂的类型转换和操作。
- keyof 操作符:keyof 操作符用于获取一个类型的所有属性名,它返回一个由属性名组成的联合类型。在Mapped Types中,我们经常使用keyof操作符来遍历和操作类型的属性。
keyof
interface Person {
name: string;
age: number;
address: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "address"
function getProperty(obj: Person, key: keyof Person) {
return obj[key];
}
const person: Person = {
name: "John",
age: 30,
address: "123 Main St"
};
console.log(getProperty(person, "name")); // Output: "John"
console.log(getProperty(person, "age")); // Output: 30
console.log(getProperty(person, "address")); // Output: "123 Main St"
在上面的示例中,我们使用keyof
操作符获取了Person
接口的所有属性名,并将其存储在PersonKeys类型中。然后,我们定义了一个名为getProperty
的函数,它接受一个对象和一个属性名作为参数,并返回该属性的值。
- infer 关键字:infer 关键字用于在条件类型中推断类型变量。它允许我们从一个类型中提取出另一个类型,并将其用于条件类型的结果。infer 关键字通常与条件类型和Mapped Types一起使用,以实现类型的推断和转换。
infer关键字示例
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function sum(a: number, b: number): number {
return a + b;
}
type SumReturnType = ReturnType<typeof sum>; // number
console.log(typeof sum(2, 3)); // Output: "number"
在上面的示例中,我们定义了一个名为ReturnType的类型,它接受一个函数类型T作为参数。我们使用条件类型和infer关键字来推断函数的返回类型。然后,我们定义了一个名为sum的函数,并使用SumReturnType类型来推断sum函数的返回类型。
- 索引类型(Index Types):索引类型允许我们通过索引访问类型的属性。在Mapped Types中,我们可以使用索引类型来动态地访问和操作类型的属性。
索引类型示例
interface Dictionary<T> {
[key: string]: T;
}
const dictionary: Dictionary<number> = {
a: 1,
b: 2,
c: 3
};
console.log(dictionary["a"]); // Output: 1
console.log(dictionary["b"]); // Output: 2
console.log(dictionary["c"]); // Output: 3
我们定义了一个名为Dictionary的接口,它使用索引类型来定义一个字符串索引和对应的值类型。然后,我们创建了一个名为dictionary的对象,它的键是字符串,值是数字类型。我们可以使用索引访问运算符来访问和操作dictionary对象的属性。
- 映射类型的递归和嵌套:Mapped Types可以嵌套和递归使用,以创建更复杂的类型转换和操作。通过嵌套和递归使用Mapped Types,我们可以实现更高级的类型转换和修改。