ts 終於搞懂TS中的泛型啦! | typescript 入門指南 04

程式設計師王天發表於2023-10-17

大家好,我是王天~

這篇文章是 ts入門指南系列中第四篇,主要講解ts中的泛型應用,泛型在ts中是比較重要的概念,我花挺長時間才搞明白的,希望能幫助到大家 ~

** ts 入門指南系列 **

  1. Ts和Js 誰更適合前端開發?| typescript 入門指南 01
  2. 詳解tsconfig.json 配置檔案 | 02 ts入門指南
  3. ts基礎使用-語法型別 | typescript入門指南 03

1. 前言

我們可以把泛型比喻為一個型別佔位符,它告訴編譯器:“嘿,這裡有一個型別引數,我現在不確定具體是什麼型別,但稍後會告訴你。”

透過使用泛型,我們可以編寫更靈活、更可複用的程式碼。它允許我們在定義函式、類或介面時使用型別佔位符來表示型別,而不直接指定具體的型別。這樣,在實際使用時,我們可以傳入不同的型別引數,使得程式碼可以適用於多種情況。

例如,讓我們看一個簡單的例子,來解釋泛型的使用。假設我們有一個名為 identity 的函式,它接受一個引數並返回該引數:

function identity<T>(value: T): T {
  return value;
}

來源於網路 圖片來源網路

在上述程式碼中,<T> 表示這是一個泛型函式,T 是一個型別引數,可以是任何型別。函式的引數 arg 的型別為 T,返回值的型別也是 T

這樣,我們可以在函式呼叫時傳入不同的型別引數,使得函式適用於各種型別的引數。

例如,我們可以這樣呼叫 identity 函式:

let result1 = identity<number>(42); // 傳入 number 型別
let result2 = identity<string>("Hello, TypeScript"); // 傳入 string 型別

在第一次呼叫時,型別引數 number 被傳遞給 identity 函式,所以返回值的型別也是 number。而在第二次呼叫時,型別引數 string 被傳遞給 identity 函式,所以返回值的型別是 string

透過使用泛型,我們可以編寫出更加通用的函式,不限於特定的型別。這樣一來,我們能夠避免程式碼的重複編寫,提高程式碼的可複用性和靈活性。

泛型主要用在四個場合:函式、介面、類和別名。

基本使用

泛型使用尖括號 <T> 來表示,並在定義函式、類或介面時指定型別引數。下面是一些基本的使用示例:

// 示例1: 建立一個泛型函式
function identity<T>(arg: T): T {
  return arg;
}

// 示例2: 使用泛型函式
let output = identity<string>("Hello");
console.log(output); // 輸出: Hello

// 示例3: 使用型別推斷,自動推斷泛型型別
let output2 = identity("Hello");
console.log(output2); // 輸出: Hello

在示例 1 中,函式 identity 使用了泛型型別引數 T,表示引數和返回值的型別可以是任何型別。示例 2 和示例 3 展示瞭如何使用泛型函式並指定引數的型別。

2. 使用泛型變數:

泛型變數允許我們在函式或類中使用一種不確定的型別,而在實際使用時才確定具體的型別。

舉個例子,考慮一個簡單的函式identity,它接受一個引數並返回相同的值:

function identity<T>(arg: T): T {
  return arg;
}

在這個例子中,我們使用了泛型變數T,它可以代表任意型別。當我們呼叫函式identity時,編譯器會根據傳入的引數型別自動推斷T的具體型別。

例如:

let result = identity<string>("Hello");
console.log(result); // 輸出:Hello

let value = identity<number>(42);
console.log(value); // 輸出:42

透過使用泛型變數,函式identity可以適用於不同型別的引數,提供了更高的靈活性和可重用性。

3. 泛型型別:

泛型型別允許我們建立可以適用於不同型別的變數、函式或類。

舉個例子,考慮一個簡單的陣列反轉函式reverse

function reverse<T>(array: T[]): T[] {
  return array.reverse();
}

在這個例子中,我們定義了一個泛型函式reverse,接受一個陣列引數,並返回反轉後的陣列。泛型型別T用於指定陣列的元素型別。

例如:

let numbers: number[] = [1, 2, 3, 4, 5];
let reversedNumbers = reverse(numbers);
console.log(reversedNumbers); // 輸出:[5, 4, 3, 2, 1]

let strings: string[] = ["apple", "banana", "orange"];
let reversedStrings = reverse(strings);
console.log(reversedStrings); // 輸出:["orange", "banana", "apple"]

透過使用泛型型別,函式reverse可以適用於不同型別的陣列,提供了更高的靈活性和可重用性。

4. 泛型類:

泛型類允許我們建立可以適用於多種型別的類。類中的成員可以使用泛型型別進行宣告和使用。

舉個例子,考慮一個簡單的Box類,用於儲存任意型別的值:

class Box<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }
}

在這個例子中,我們定義了一個泛型類Box,它具有一個私有成員value和一個公共方法getValue用於獲取值。

例如:

let box1 = new Box<number>(42);
console.log(box1.getValue()); // 輸出:42

let box2 = new Box<string>("Hello");
console.log(box2.getValue()); // 輸出:Hello

過使用泛型引數<T>,可以在類的定義中引入型別變數來表示未知的型別。這樣一來,我們可以在類例項化時指定具體的型別,從而建立適用於不同型別資料的類的例項。

5. 泛型約束:

泛型約束允許我們限制泛型型別的範圍,使其滿足特定條件

5.1. 確保屬性存在

舉個例子,假設我們想編寫一個函式getLength,用於獲取物件的長度。但是並不是所有的物件都有length屬性,所以我們需要對泛型型別進行約束,確保它具有該屬性。

例如:

interface HasLength {
  length: number;
}

function getLength<T extends HasLength>(obj: T): number {
  return obj.length;
}

在這個例子中,我們使用泛型約束T extends HasLength來限制泛型型別T必須滿足HasLength介面的要求,即具有length屬性。

例如:

let str = "Hello";
console.log(getLength(str)); // 輸出:5

let arr = [1, 2, 3, 4, 5];
console.log(getLength(arr)); // 輸出:5

透過使用泛型約束,函式getLength可以接受具有length屬性的物件,並返回其長度。

5.2 檢查物件的 key

1、keyof typescript 中檢測型別的方法,以聯合型別的方式方返回型別的所有 key
2、搭配泛型約、<T,K extends keyof T >

refshttps://juejin.cn/post/6844904184894980104#heading-0
使用泛型,可以讓我們在編譯前發現錯誤。

6 泛型介面:

泛型介面允許我們定義可以適用於不同型別的介面。

舉個例子,考慮一個簡單的Transformer介面,它定義了一個將輸入值轉換為輸出值的轉換器:

interface Transformer<T, U> {
  transform(input: T): U;
}

在這個例子中,我們定義了一個泛型介面Transformer,它有兩個型別引數TU,用於定義輸入型別和輸出型別。

例如,我們可以實現一個字串到數字的轉換器:

class StringToNumberTransformer implements Transformer<string, number> {
  transform(input: string): number {
    return parseFloat(input);
  }
}

透過定義實現了Transformer介面的類,我們可以建立不同型別的轉換器。

例如:

let transformer = new StringToNumberTransformer();
let result = transformer.transform("3.14");
console.log(result); // 輸出:3.14

透過使用泛型介面,我們可以定義可重用、可靈活的介面,適用於不同型別的轉換操作。

介面搭配泛型,應用在 calss 類上

extend people 約束泛型類 在 people 介面範圍內
此時是 泛型變數佔位符,在例項化 class 類是傳遞型別

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

class Popele<T extends people> {
  data: T;
  constructor(data: T) {
    this.data = data;
  }
  hi() {
    return `${this.data.name},,${this.data.age}`;
  }
}

let zhagnsan = new Popele<people>({ name: "張三", age: 18 });

總結

泛型在 TypeScript 中提供了更靈活、可重用的程式碼編寫方式。它可以用於定義函式、類以及介面,讓我們能夠編寫適用於不同型別的程式碼。

讀者朋友好呀,我是王天~

嘗試做過很多事情,汽修專業肄業生,半路出道的野生程式設計師、前端講師、新手作者,最終還是喜歡寫程式碼、樂於用文字記錄熱衷分享~

如文章有錯誤或者不嚴謹的地方,期待給於指正,萬分感謝。

如果喜歡或者 有所啟發,歡迎 star,對作者也是一種鼓勵。

微信:「wangtian3111」,加我進王天唯一的讀者群。

個人部落格:https://itwangtian.com

相關文章