TypeScript - 泛型約束



在 TypeScript 中,泛型約束允許你指定對可與泛型型別引數一起使用的型別的限制。這透過確保泛型程式碼僅適用於相容的資料型別來增加一層型別安全。

問題示例

在深入研究泛型約束之前,讓我們瞭解需要應用泛型約束的問題示例。

示例

在下面的程式碼中,我們建立了merge()泛型函式,它將兩個物件作為引數,使用擴充套件運算符合並它們,並返回合併後的物件。

之後,我們透過傳遞兩個物件作為引數來呼叫merge()函式,它成功地列印了合併後的物件。

// Generic function to merge two objects
function merge<T, U>(obj1: T, obj2: U) {
    return {...obj1, ...obj2};
}

// Invoke the function
const mergedObj = merge({name: 'Sam'}, {age: 30});
console.log(mergedObj); // Output: {name: 'Sam', age: 30}  

編譯後,它將生成以下 JavaScript 程式碼。

// Generic function to merge two objects
function merge(obj1, obj2) {
    return Object.assign(Object.assign({}, obj1), obj2);
}
// Invoke the function
const mergedObj = merge({ name: 'Sam' }, { age: 30 });
console.log(mergedObj); // Output: {name: 'Sam', age: 30}  

輸出

上面示例程式碼的輸出如下:

{name: 'Sam', age: 30}

merge()函式具有泛型引數。因此,它可以接受任何資料型別的數值作為引數,包括物件。如果你傳遞布林值作為第二個引數會怎樣?讓我們看看下面的例子。

示例

下面的示例程式碼與之前的程式碼非常相似。我們只是在呼叫時將merge()函式的第二個引數更改為布林值。

// Generic function to merge two objects
function merge<T, U>(obj1: T, obj2: U) {
    return {...obj1, ...obj2};
}

// Invoke the function
const mergedObj = merge({name: 'Sam'}, true);
console.log(mergedObj); // Output: {name: 'Sam'}  

編譯後,它將生成以下 JavaScript 程式碼。

// Generic function to merge two objects
function merge(obj1, obj2) {
    return Object.assign(Object.assign({}, obj1), obj2);
}
// Invoke the function
const mergedObj = merge({ name: 'Sam' }, true);
console.log(mergedObj); // Output: {name: 'Sam'}  

輸出

上面示例程式碼的輸出如下:

{name: 'Sam'}

上面的程式碼只打印第一個物件,因為第二個引數是布林值而不是物件。為了解決上面示例程式碼中發現的問題,開發人員可以使用泛型約束。

TypeScript 中泛型約束的工作原理?

泛型約束允許我們將泛型引數限制為僅接受特定型別的數值。即你可以縮小泛型引數的型別。

語法

您可以按照以下語法使用泛型引數的泛型約束。

function merge<T extends object>(obj1: T) {
    // Code to execute
}
  • 在上面的語法中,“T”是一個泛型型別,“extends”是一個關鍵字,“object”是一個數據型別。

  • 在這裡,“T”只接受具有“object”資料型別的數值。

讓我們透過下面的例子來更多地瞭解泛型約束。現在,如果你嘗試編譯下面的程式碼,你將得到編譯錯誤,因為泛型引數只能接受物件引數,但我們傳遞的是布林值。

// Generic function to merge two objects
function merge<T extends object, U extends object>(obj1: T, obj2: U) {
    return { ...obj1, ...obj2 };
}

// Invoke the function
const mergedObj = merge({ name: 'Sam' }, true);
console.log(mergedObj);

編譯上面的 TypeScript 程式碼時,編譯器會顯示以下錯誤:

Argument of type 'boolean' is not assignable to parameter of type 'object'.

這樣,我們可以限制泛型引數以接受特定資料型別的數值。

示例(使用介面擴充套件泛型型別)

讓我們逐步瞭解下面的程式碼。

  • 我們定義了包含 name、age 和 email 屬性的“Person”介面。

  • 接下來,我們定義了包含“empCode”和“empDept”屬性的“Employee”介面。

  • merge()函式包含兩個泛型引數 T(Person 型別)和 U(Employee 型別)。

  • merge()函式中,我們合併兩個物件。

  • 之後,我們分別定義了兩個 Person 和 Employee 型別的物件。

  • 接下來,我們透過傳遞物件作為引數來呼叫merge()函式,程式碼執行沒有任何錯誤。

// Define Person interface
interface Person {
    name: string;
    age: number;
    email: string;
}

// Define Employee interface
interface Employee {
    empCode: number;
    empDept: string;
}

// Generic function which takes Objects of the Person and Employee interfaces types
function merge<T extends Person, U extends Employee>(obj1: T, obj2: U) {
    return { ...obj1, ...obj2 };
}

// Create two objects
const person: Person = { name: 'John', age: 30, email: 'abc@gmail.com' };
const employee: Employee = { empCode: 1001, empDept: 'IT' };

// Invoke the function
const mergedObj = merge(person, employee);
console.log(mergedObj);

編譯後,它將生成以下 JavaScript 程式碼。

// Generic function which takes Objects of the Person and Employee interfaces types
function merge(obj1, obj2) {
    return Object.assign(Object.assign({}, obj1), obj2);
}
// Create two objects
const person = { name: 'John', age: 30, email: 'abc@gmail.com' };
const employee = { empCode: 1001, empDept: 'IT' };
// Invoke the function
const mergedObj = merge(person, employee);
console.log(mergedObj);

輸出

上面示例程式碼的輸出如下:

{
  name: 'John',
  age: 30,
  email: 'abc@gmail.com',
  empCode: 1001,
  empDept: 'IT'
}

在泛型約束中使用型別引數

TypeScript 還允許你定義一個型別引數,該引數受同一函式的另一個引數約束。

讓我們透過下面的例子來了解它。

示例

在下面的程式碼中,型別“U”擴充套件了在第一個引數中接收的物件的鍵。因此,它將接受obj物件的鍵作為引數,以避免函式體中的錯誤。

接下來,我們透過傳遞“obj”物件作為引數來呼叫getValue()函式。它在輸出中列印鍵值。

// Parameter U ensures that the key is a valid key of the object T.
function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
    return obj[key];
}

// Define an object
const obj = {
    name: 'Sam',
    age: 34
};

// Get the value of the key 'name'
const name1 = getValue(obj, 'name');
console.log(name1); // Sam

編譯後,它將生成以下 JavaScript 程式碼。

// Parameter U ensures that the key is a valid key of the object T.
function getValue(obj, key) {
    return obj[key];
}
// Define an object
const obj = {
    name: 'Sam',
    age: 34
};
// Get the value of the key 'name'
const name1 = getValue(obj, 'name');
console.log(name1); // Sam

輸出

上面示例程式碼的輸出如下:

Sam

我們瞭解到泛型約束對於接受特定資料型別的數值作為引數而不是接受所有資料型別的數值很有用。

廣告