如何为泛型添加约束?

typescriptweb developmentfront end technology

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 {
    // 函数体
}
  • functionName − 使用带有约束的泛型类型的函数名称

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

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

  • : ReturnType − 函数的返回类型。

  • 函数体 − 函数的实现,它使用受指定 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 关键字,并在其后添加要将泛型约束为的类型或接口,开发者可以创建更可靠、更易于维护的代码。


相关文章