注意: 如果您是 TypeScript 新手,请先查看我们的 TypeScript 入门 教程。
TypeScript 实用类型是内置的辅助工具,可以更轻松地处理和转换数据类型。它们在处理对象和联合类型时特别有用。
实用类型通过使您能够从现有类型创建新类型来帮助您编写更安全、更简洁、更灵活的代码。这些新类型可能与原始对象或类型不同,通过
- 使属性可选/必填/只读,或者
- 选择和省略字段,或者
- 为现有类型添加新功能。
使用的实用类型
下表列出了一些常见的实用类型
实用类型 | 描述 |
---|---|
Partial<Type> |
使所有属性都可选。 |
Required<Type> |
使所有属性都必填。 |
Readonly<Type> |
使所有属性都只读。 |
Pick<Type, Keys> |
只保留选定的属性。 |
Omit<Type, Keys> |
删除选定的属性。 |
Record<Keys, Type> |
创建具有特定键和类型的对象。 |
Exclude<UnionType, ExcludedMembers> |
从联合类型中删除特定类型。 |
Extract<Type, Union> |
从联合类型中提取特定类型。 |
ReturnType<Type> |
获取函数的返回类型。 |
Parameters<Type> |
将函数的参数类型获取为元组。 |
ConstructorParameters<Type> |
获取类的构造函数的参数类型。 |
现在,让我们逐一看看这些实用类型。
1. Partial
Partial
类型使类型中的所有属性都成为可选的。当您只需要对象中的某些属性时,可以使用它。
示例
// Interface with 3 required properties
interface Student {
id: number;
name: string;
gpa: number;
}
// Create a partial type from Student
// All Student properties are now optional
type PartialStudent = Partial<Student>;
// Object with only id property
let student1: PartialStudent = {id: 101};
console.log(student1);
// Object with only name and gpa properties
let student2: PartialStudent = {name: "Peter Parker", gpa: 4};
console.log(student2);
// Object with only id and gpa properties
let student3: Partial<Student> = {id: 105, gpa: 3.5};
console.log(student3);
输出
{ id: 101 } { name: 'Peter Parker', gpa: 4 } { id: 105, gpa: 3.5 }
在这里,Student
接口有三个必需属性:id
、name
和 gpa
。因此,您无法创建不包含这些属性的 Student
对象。
// Error: name and gpa are missing
let student: Student = {id: 101};
但是,我们可以通过创建 Student
的部分类型来完成此任务。
type PartialStudent = Partial<Student>;
因此,PartialStudent
类似于 Student
类型的版本,但其所有属性都是可选的。
请注意我们程序中的以下代码
let student3: Partial<Student> = {id: 105, gpa: 3.5};
此代码等同于
let student3: PartialStudent = {id: 105, gpa: 3.5};
2. Required
Required
类型使类型中的所有属性都成为必填项。当所有属性都必填时使用它。
示例
// Interface with optional properties id and gpa
interface Student {
id?: number;
name: string;
gpa?: number;
}
// Create a required type from Student
// All Student properties are now required
type RequiredStudent = Required<Student>;
// Create object of RequiredStudent type
let student1: RequiredStudent = {id: 101, name: "Peter Parker", gpa: 4};
console.log(student1);
// Error: RequiredStudent object initialized without id property
// let student2: RequiredStudent = {name: "Peter Parker", gpa: 4};
输出
{ id: 101, name: 'Peter Parker', gpa: 4 }
在这里,Student
接口有两个可选属性:id
和 gpa
。
但是,RequiredStudent
类型使这些属性成为必填项。
type RequiredStudent = Required<Student>;
因此,您无法创建如下对象
// Error: RequiredStudent object initialized without id property
let student2: RequiredStudent = {name: "Peter Parker", gpa: 4};
3. Readonly
Readonly
类型使类型中的所有属性都变为只读(无法更改)。当您需要保护数据免遭意外更改时,请使用它。
示例
type Student = {
id: number;
name: string;
}
// Use Readonly to create an object
let student1: Readonly<Student> = {id: 101, name: "Peter Parker"};
console.log(student1);
// Error: 'name' is a read-only property
// student1.name = "Gwen Stacy";
输出
{ id: 101, name: 'Peter Parker' }
在这里,student1
的类型是 Readonly<Student>
。
let student1: Readonly<Student> = {id: 101, name: "Peter Parker"};
因此,在初始化后,其属性将无法更改。
// Error: 'name' is a read-only property
student1.name = "Gwen Stacy";
4. Pick
Pick
创建一个只包含指定属性的类型。当您只需要一个大型类型中的少数几个字段时,可以使用它。
示例
type Student = {
id: number;
name: string;
gpa: number;
phone: number;
}
// Create a type that only has id and gpa
type IdGPA = Pick<Student, "id" | "gpa">;
// Create another type that only has name
type StudentName = Pick<Student, "name">;
// Create object of IdGPA type
let student1: IdGPA = {id: 101, gpa: 2.6};
console.log(student1);
// Create object of StudentName type
let student2: StudentName = {name: "Peter Parker"};
console.log(student2);
输出
{ id: 101, gpa: 2.6 } { name: 'Peter Parker' }
在这里,Student
类型有四个属性:id
、name
、gpa
和 phone
。
然后我们从 Student
创建了两个不同的类型。
IdGPA
- 只包含id
和gpa
属性。StudentName
- 只包含name
属性。
type IdGPA = Pick<Student, "id" | "gpa">;
type StudentName = Pick<Student, "name">;
如您所见,您可以使用 |
运算符在 Pick
类型中添加更多属性。
5. Omit
Omit
通过删除指定属性来创建类型。
示例
type Student = {
id: number;
name: string;
gpa: number;
phone: number;
}
// Create a type that doesn't have id
type NoId = Omit<Student, "id">;
// Create another type that doesn't have gpa and phone
type IdName = Omit<Student, "gpa" | "phone">;
// Create object of NoId type
let student1: NoId = {name: "Peter Parker", gpa: 4, phone: 5786389};
console.log(student1);
// Create object of IdName type
let student2: IdName = {id: 103, name: "Gwen Stacy"};
console.log(student2);
输出
{ name: 'Peter Parker', gpa: 4, phone: 5786389 } { id: 103, name: 'Gwen Stacy' }
在这里,Student
类型有四个属性:id
、name
、gpa
和 phone
。
然后我们从 Student
创建了两个不同的类型。
NoId
- 省略id
属性。因此,它只包含name
、gpa
和phone
属性。IdName
- 省略gpa
和phone
属性。因此,它只包含id
和name
属性。
type NoId = Omit<Student, "id">;
type IdName = Omit<Student, "gpa" | "phone">;
6. Record
Record
创建一个具有特定键和值类型的对象类型。当您知道所有键并且希望它们遵循某种模式时,可以使用它。
示例
type Party = "Democrat" | "Republican" | "Independent";
// New type where Party values are keys
// Each key has a numerical value
type VoteCount = Record<Party, number>;
let presidentialVote: VoteCount = {
Democrat: 71,
Republican: 96,
Independent: 2
};
console.log(presidentialVote);
输出
{ Democrat: 71, Republican: 96, Independent: 2 }
在这里,Party
类型可以具有以下三个值之一:“Democrat”、“Republican” 或 “Independent”。
然后,我们使用 Record
创建了一个 VoteCount
类型,该类型以以下方式存储键值对:
- 键:
Party
类型允许的值作为键。 - 值:每个键都分配了一个数值。
type VoteCount = Record<Party, number>;
然后我们创建了一个 VoteCount
类型的 presidentialVote
对象。
let presidentialVote: VoteCount = {
Democrat: 71,
Republican: 96,
Independent: 2
};
下面给出了 presidentialVote
的键值对:
键 | 值 |
---|---|
Democrat |
71 |
Republican |
96 |
Independent |
2 |
7. Exclude
Exclude
通过从联合类型中删除特定类型来创建类型。此类型有助于缩小可能值的范围。
示例
type OrderStatus = "cart" | "bought" | "cancelled" | "error";
// Create a new type by excluding "error" status
type ValidStatus = Exclude<OrderStatus, "error">;
// Valid code
let order1: ValidStatus = "bought";
let order2: ValidStatus = "cancelled";
let order3: ValidStatus = "cart";
// Invalid code because "error" doesn't exist in ValidStatus
// let order4: ValidStatus = "error";
console.log(order1);
console.log(order2);
console.log(order3);
输出
bought cancelled cart
在这里,ValidStatus
类型具有 OrderStatus
的所有值,但不包括“error”类型。
type OrderStatus = "cart" | "bought" | "cancelled" | "error";
type ValidStatus = Exclude<OrderStatus, "error">;
您可以使用 |
运算符添加更多排除项。
Exclude<OrderStatus, "cancelled" | "error">;
上面的代码排除了 "cancelled"
和 "error"
。
8. Extract
Extract
通过从联合类型中提取特定类型来创建类型。此类型有助于缩小可能值的范围。
示例
type OrderStatus = "cart" | "bought" | "cancelled" | "error";
// Create a new type by extracting "bought" status
type Bought = Extract<OrderStatus, "bought">;
// Create object of Bought type
let order1: Bought = "bought";
console.log(order1);
// Invalid: "cart" is absent in Bought type
// let order2: Bought = "cart";
// Output: bought
在这里,Bought
类型只能存储 "bought"
。
type OrderStatus = "cart" | "bought" | "cancelled" | "error";
type Bought = Extract<OrderStatus, "bought">;
您可以使用 |
运算符提取更多值。
Extract<OrderStatus, "cancelled" | "error">;
上面的代码创建了一个只能存储 "cancelled"
和 "error"
的类型。
9. ReturnType
ReturnType
获取函数的返回类型。
示例
function greet(name: string): string {
return `Welcome, ${name}!`;
}
// Get the return type of greet() i.e. string
type GreetReturn = ReturnType<typeof greet>;
// Create a variable of GreetReturn type
let message: GreetReturn = greet("Lord Vader");
console.log(message);
输出
Welcome, Lord Vader!
在这里,greet()
函数返回一个字符串。因此,GreetReturn
类型将与 string
类型相同。
因此,以下代码
let message: GreetReturn = greet("Lord Vader");
等同于
let message: string = greet("Lord Vader");
10. Parameters
Parameters
实用类型将函数的参数类型获取为元组。
function greet(name: string, age: number): string {
return `Welcome, ${name}. You are ${age} years old!`;
}
// GreetParams is a [string, number] tuple
type GreetParams = Parameters<typeof greet>;
// Create a variable of GreetParams type
let personInfo: GreetParams = ["Vader", 45];
// Print the tuple elements
console.log(personInfo[0]);
console.log(personInfo[1]);
// Pass the tuple elements as arguments to greet()
let message: string = greet(personInfo[0], personInfo[1]);
console.log(message);
输出
Vader 45 Welcome, Vader. You are 45 years old!
在这里,GreetParams
类型是一个元组,由 greet()
函数的参数类型组成。
由于 greet()
函数有一个 string
和 number
参数,因此 GreetParams
将是一个 [string, number]
类型的元组。
11. ConstructorParameters
ConstructorParameters
类型类似于 Parameters
,不同之处在于它返回对象构造函数的参数类型。
示例
class Person {
constructor(private name: string, private age: number) {}
greet(): void {
console.log(`Welcome, ${this.name}. You are ${this.age} years old!`);
}
}
// PersonParams is a [string, number] tuple
type PersonParams = ConstructorParameters<typeof Person>;
// Create a variable of PersonParams type
let personInfo: PersonParams = ["Vader", 45];
// Print the tuple elements
console.log(personInfo[0]);
console.log(personInfo[1]);
// Create an instance of Person and pass the
// Tuple elements as arguments to the constructor
let person1 = new Person(personInfo[0], personInfo[1]);
person1.greet();
输出
Vader 45 Welcome, Vader. You are 45 years old!
在这里,PersonParams
类型是一个元组,由 Person
的构造函数参数类型组成。
TypeScript 实用类型的更多信息
不,JavaScript 没有实用类型,因为它是一种动态类型语言。因此,JavaScript 在运行时而不是编译时检查类型,这使得捕获错误代码变得困难。
相比之下,TypeScript 在编译时检查类型。因此,TypeScript 提供的实用类型有助于缩小您想在代码中使用的类型。
如果您使用了不正确的类型,您可以在编译时自行纠正,因为 TypeScript 会通知您错误。例如,
JavaScript 代码
// No type checks or utility types
let student = { id: 101, name: "Peter Parker" };
// Allowed, but may cause bugs
student.name = 42;
TypeScript 代码
interface Student {
id?: number;
name?: string;
}
// Error at compile time because 'name' cannot be a number
let user: Required<Student> = { id: 101, name: 42 };
实用类型具有以下优点:
- 减少重复:它们可以帮助您避免一遍又一遍地编写相似的类型定义。
- 类型安全:TypeScript 会在代码运行之前尽早捕获错误。
- 灵活性:随着代码的变化,您可以轻松调整类型。
您也可以在 TypeScript 中创建自己的实用类型。例如,
// Create a custom type
type CustomUnion<T> = T | boolean;
// Same as string | boolean
type StringBool = CustomUnion<string>;
// Same as number | boolean
type NumBool = CustomUnion<number>;
// StringBool can take string or boolean data
let var1: StringBool = "String Data";
let var2: StringBool = false;
console.log(var1);
console.log(var2);
// NumBool can take number or boolean data
let var3: NumBool = 75;
let var4: NumBool = true;
console.log(var3);
console.log(var4);
输出
String Data false 75 true
在上面的程序中,请注意以下代码:
type CustomUnion<T> = T | boolean;
在这里,T
是您想指定的类型的占位符,而 boolean
类型始终是联合类型的一部分。因此,
CustomUnion<string>
- 为您提供string | boolean
的联合类型。CustomUnion<number>
- 为您提供number | boolean
的联合类型。
您也可以根据需要使用其他占位符。例如,
type CustomUnion<T, U> = T | U | boolean;
另请阅读