如何为泛型添加约束?


TypeScript 是一种开源编程语言,它为 JavaScript 提供了类型注解。TypeScript 具有泛型类型,使开发人员能够编写可与不同数据类型一起使用的代码。泛型为代码提供了灵活性和可重用性。但是,在某些情况下,需要为泛型类型添加约束以限制可与其一起使用的类型。

在本文中,我们将解释如何在 TypeScript 中为泛型类型添加约束,并将讨论一些示例。

什么是泛型类型?

在我们深入了解约束之前,让我们首先了解什么是泛型类型。TypeScript 中的泛型类型允许开发人员定义可以与各种数据类型一起使用的类型。这意味着函数、类或接口可以接收任何类型的数据,从而使代码更加灵活和可重用。

例如,让我们考虑一个简单的函数,它接收两个参数并返回这两个参数的总和。

function add(a, b) {
   return a + b;
}

此函数对于数字有效,但是如果我们想添加两个字符串或两个数组呢?我们将不得不为每种数据类型编写单独的函数,这效率不高。相反,我们可以使用泛型类型来创建一个可用于任何数据类型的函数。

function add<T>(a: T, b: T): T {
   return a + b;
}

这里,函数 **add** 具有泛型类型 **T**,它可以表示任何数据类型。该函数接收两个类型为 **T** 的参数并返回类型为 **T** 的值。

为泛型类型添加约束

虽然泛型类型提供了灵活性,但在某些情况下,我们可能希望添加约束来限制可与其一起使用的类型。添加约束可以提高代码质量,并帮助在编译时而不是运行时捕获错误。

约束是通过使用关键字 **extends** 后跟我们要将泛型类型约束到的类型或接口来添加的。

语法

function functionName<T extends ConstraintType>(arg1: Arg1Type, arg2: Arg2Type, ...): ReturnType {
   // function body
}
  • **functionName** - 使用具有约束的泛型类型的函数的名称

  • **<T extends ConstraintType>** - 定义泛型类型 T 并使用 extends 关键字指定约束。ConstraintType 可以是 TypeScript 中任何有效的类型,包括接口、类和基本类型。

  • **(arg1: Arg1Type, arg2: Arg2Type, ...)** - 传递给函数的参数及其类型。

  • **: ReturnType** - 函数的返回类型。

  • **function body** - 函数的实现,它使用受指定 ConstraintType 约束的泛型类型 T。

需要注意的是,约束只能应用于函数中的一个泛型类型。如果使用了多个泛型类型,则每个泛型类型必须有自己的约束。此外,可以通过使用交集类型在一个泛型类型上有多个约束。

示例

示例 1:数组上的约束

function getLength<T extends Array<any>>(arr: T): number {
   return arr.length;
}

在此示例中,我们使用 **extends** 关键字为泛型类型 **T** 添加了约束。约束指定类型 **T** 必须扩展 **Array<any>>** 类型。这意味着函数 **getLength** 只能与数组一起使用。

我们可以举一个例子来验证这一点。

function getLength<T extends Array<any>>(arr: T): number {
   return arr.length;
}
const arr = [6, 7];
console.log(`The length of the array is: ${getLength(arr)}`);

编译后,它将生成以下 JavaScript 代码:

function getLength(arr) {
   return arr.length;
}
var arr = [6, 7];
console.log("The length of the array is: ".concat(getLength(arr)));

输出

The length of the array is: 2

示例 2:对象上的约束

在此示例中,我们使用 **extends** 关键字为两个泛型类型 **T** 和 **K** 添加了约束。约束指定类型 **T** 必须扩展 Person 接口,类型 **K** 必须扩展类型 **T** 的键。这意味着函数 **getProperty** 只能与类型为 **Person** 的对象和该接口中存在的键一起使用。

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

function getProperty<T extends Person, K extends keyof T>(obj: T, key: K) {
   return obj[key];
}

const person: Person = {
   name: "John",
   age: 21,
};

console.log(`The name of the person is: ${getProperty(person, "name")}`);

编译后,它将生成以下 JavaScript 代码:

function getProperty(obj, key) {
   return obj[key];
}
var person = {
   name: "John",
   age: 21
};
console.log("The name of the person is: ".concat(getProperty(person, "name")));

输出

The name of the person is: John

示例 3:类上的约束

在此示例中,我们使用 **extends** 关键字为泛型类型 **T** 添加了约束。约束指定类型 **T** 必须扩展 **Animal** 类。这意味着类 **Zoo** 只能与动物对象一起使用。

Class Animl {
}
class Animal {
   name: string;
   constructor(name: string) {
      this.name = name;
   }
}
class Zoo<T extends Animal> {
   animals: T[];
   constructor(animals: T[]) {
      this.animals = animals;
   }
}
const lion = new Animal('Lion');
const tiger = new Animal('Tiger');
const zoo = new Zoo<Animal>([lion, tiger]);
const zoo1 = new Zoo<Animl>([lion, tiger])  // gives error

输出

编译后,它将生成以下错误:

Type 'Animl' does not satisfy the constraint 'Animal'.
Property 'name' is missing in type 'Animl' but required in type 'Animal'.

示例 4:函数上的约束

在此示例中,我们使用 **extends** 关键字为泛型类型 **T** 添加了约束。约束指定类型 **T** 必须是任何可以与 **Array** 类型一起使用的类型。这意味着函数 **filter** 只能与数组一起使用,第二个参数 **predicate** 必须是一个接收类型为 **T** 的项并返回布尔值的函数。

function filter<T>(array: T[], predicate: (item: T) => boolean): T[] {
   return array.filter(predicate);
}
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = filter(numbers, (item) => item % 2 === 0);
console.log(`The numbers afer applying filter are: ${evenNumbers}`);

编译后,它将生成以下 JavaScript 代码:

function filter(array, predicate) {
   return array.filter(predicate);
}
var numbers = [1, 2, 3, 4, 5];
var evenNumbers = filter(numbers, function (item) { return item % 2 === 0; });
console.log("The numbers afer applying filter are: ".concat(evenNumbers));

输出

The numbers afer applying filter are: 2,4

结论

总之,在 TypeScript 中为泛型类型添加约束是一个强大的功能,可以帮助开发人员编写更健壮和灵活的代码。可以添加约束来限制可与泛型类型一起使用的类型,这可以提高代码质量并在编译时捕获错误。通过使用 extends 关键字后跟我们要将泛型类型约束到的类型或接口,开发人员可以创建更可靠且易于维护的代码。

更新于: 2023年8月22日

400 次查看

开启你的 职业生涯

通过完成课程获得认证

开始学习
广告

© . All rights reserved.