TypeScript 实用工具类型

注意: 如果您是 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 接口有三个必需属性:idnamegpa。因此,您无法创建不包含这些属性的 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 接口有两个可选属性:idgpa

但是,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 类型有四个属性:idnamegpaphone

然后我们从 Student 创建了两个不同的类型。

  • IdGPA - 只包含 idgpa 属性。
  • 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 类型有四个属性:idnamegpaphone

然后我们从 Student 创建了两个不同的类型。

  • NoId - 省略 id 属性。因此,它只包含 namegpaphone 属性。
  • IdName - 省略 gpaphone 属性。因此,它只包含 idname 属性。
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() 函数有一个 stringnumber 参数,因此 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 没有实用类型,因为它是一种动态类型语言。因此,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;

另请阅读

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战