如何為泛型新增約束?


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.