iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Modern Web

TypeScript 魔法 - 喚醒你的程式碼靈感系列 第 25

Day25 - 泛型(Generics)上篇 - 讓程式碼變得更通用!

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20231010/20152047ttWafnEsai.png

什麼是泛型?

泛型是一個強大的程式設計概念,它允許我們建立通用的、可重用的程式碼。它的優點不僅在於它可以適用於不同的資料型別,還在於它提供了靜態型別檢查的好處。在 TypeScript 中,我們可以利用泛型來建立更通用的函式、類別和介面,從而使程式碼更具彈性和可維護性。

如何使用泛型?

要定義一個泛型型別,我們可以使用尖括號 <T>,其中 T 是型別參數。

未使用泛型型別

當我們初始化一個陣列時,TypeScript 根據提供的初始值來推斷陣列中元素的型別。在程式碼中,由於提供的初始值都是字串,TypeScript 會自動推斷 names1 為一個 string 陣列。然而,若初始值是空陣列,TypeScript 會推斷 names2 為一個包含任何型別的 any 陣列,因為它無法從空陣列中得知元素的具體型別。

const names1 = ['肉鬆', '傑尼龜']; // string[]
const names2 = []; // any[]

使用泛型型別

在 TypeScript 中,陣列本身是一種通用型別,可以容納各種資料型別,例如字串、數字、物件等。這種彈性可以提供靈活性,但也可能導致型別錯誤。泛型型別則允許我們在建立陣列時指定元素的型別,從而提高程式碼的型別安全性。簡而言之,陣列是通用型別,而泛型型別則提供更多型別安全性。

錯誤用法:

const names: Array = ['肉鬆', '傑尼龜']; // TypeScript 報錯,泛型類型 'Array<T>' 需要 1 個型別引數

正確用法:

const names: Array<string> = ['肉鬆', '傑尼龜'];

泛型型別需要一個型別參數 T,在這個例子中,我們使用了 Array<string>,明確告訴 TypeScript 這個陣列中的元素應該是字串型別。這樣 TypeScript 就能夠正確理解程式碼並進行型別檢查。

泛型搭配聯合型別(Union Types)

const names: Array<string | number> = ['肉鬆', 30]; // 通過
const names: Array<string | number> = ['肉鬆', 30, true]; 
// TypeScript 報錯,類型 'boolean' 不可指派給類型 'string | number'

根據泛型型別參數,存取屬性或方法

因為 TypeScript 知道 names 現在是 string 型別,所以我們可以使用 push 方法來新增新的字串元素到陣列中。

const names: Array<string> = ['肉鬆'];

names.push('傑尼龜');

console.log(names); // ['肉鬆', '傑尼龜']

泛型型別的靈活性

泛型型別的真正強大之處在於它的靈活性。它允許我們將型別資訊存取在傳入資料之外,以便在使用泛型型別時能夠更好的利用 TypeScript 的支持和型別檢查功能。

const promise = new Promise((resolve) => 
  setTimeout(() => {
    resolve('完成');
  }, 1000);
});

https://ithelp.ithome.com.tw/upload/images/20231010/20152047lkKjMBMBde.png

我們可以看到 promise 變數實際上是 Promise 型別。當我們看到尖括號時,它表示這個 promise 最終得到的結果的型別資訊。然而,當我們看到 unknown 時,這是因為 TypeScript 不能完全理解我們實際上會分析的是一個字串。

讓我們修改程式碼,現在型別是 PromisePromise 就像陣列一樣可以與其他型別一起運作,因為最終它會返回某種型別的資料。在這個範例中,Promise 最終會返回一個字串。

const promise: Promise<string> = new Promise((resolve) => {
  setTimeout(() => {
    resolve('完成');
  }, 1000);
});

當然,我們也可以針對型別去存取它的屬性或方法。在這個例子中,因為我們指定了 Promise<string> 型別,所以我們可以存取這個 Promise 的方法或屬性,並對其進行操作。

const promise: Promise<string> = new Promise((resolve) => {
  setTimeout(() => {
    resolve('完成');
  }, 1000);
});

promise.then((data) => {
  data.split(' '); // data 可以使用 string 的 split 方法
});

本日重點

  1. 使用尖括號 <T> 來定義泛型型別,其中 T 是型別參數。
  2. 泛型型別也可以與聯合型別結合,以容納多種不同型別的資料。
  3. 我們可以根據型別資訊來存取方法和屬性,以提高型別安全性。

參考


上一篇
Day24 - 實作介面與類別
下一篇
Day26 - 泛型(Generics)下篇 - 泛型的多種姿態
系列文
TypeScript 魔法 - 喚醒你的程式碼靈感30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言