掌握 TypeScript 泛型(Generics) & Vue3 泛型

TypeScript Generics

前言

泛型(Generics)是 TypeScript 中很重要的特性,它可以大幅度簡化程式碼的撰寫,並提高程式碼的可讀性,但是 TypeScript 的泛型也是初學者最難理解的部分之一,所以就來寫一篇文章來介紹 TypeScript 的泛型。

什麼是泛型?

在說明泛型之前,我們先來看一下基本常見的型別宣告:

1
const myName: string = 'Ray';

上方的例子中,我們宣告了一個常數 myName,並指定型別為 string,這有助於我們在開發時,可以透過編輯器的提示來知道 myName 的型別為 string,這樣就可以避免在開發時發生型別錯誤。

Note
型別錯誤,在此是指 JavaScript 的隱含型別轉型導致的錯誤,例如:1 + '1' 會得到 '11',這是因為 JavaScript 會將數字 1 轉換為字串 '1',再進行字串相加。

除此之外,我們也可以透過函式來指定型別:

1
2
3
function sayHello(name: string): string {
return `Hello, ${name}`;
}

上方的程式碼中,意思是指宣告一個函式 sayHello,並指定參數 name 的型別為 string,回傳值的型別也為 string,這樣就可以確保在呼叫 sayHello 函式時,參數 name 的型別為 string,回傳值的型別也為 string

但是有時候我們並不知道參數的型別,你可能會想說就用 any 型別

1
2
3
function sayHello(name: any): any {
return `Hello, ${name}`;
}

但是這樣的寫法並不安全,因為 any 型別會讓 TypeScript 失去型別檢查的功能,這樣反而會增加程式碼的錯誤率以及失去使用 TypeScript 的意義。

那麼接下來就要來介紹泛型了,泛型是一種讓 TypeScript 可以在不指定型別的情況下,讓 TypeScript 自動推斷型別的一種特性,簡單來講泛型就是「型別參數化」。

什麼意思呢?就是當我們不知道參數的型別時,可以透過泛型來讓 TypeScript 自動推斷型別,這樣就可以避免使用 any 型別,讓 TypeScript 保持型別檢查的功能。

1
2
3
function sayHello<T>(name: T): T {
return `Hello, ${name}`;
}

使用方式非常簡單,只要在函式名稱後面加上 <T>,這樣就可以讓 TypeScript 知道這是一個泛型函式,而 T 就是型別參數,當我們呼叫 sayHello 函式時,TypeScript 就會自動推斷 name 的型別。

1
const result = sayHello('Ray');

上方的程式碼中,我們呼叫 sayHello 函式,並傳入字串 'Ray',這樣 TypeScript 就會自動推斷 name 的型別為 string,而 result 的型別也會是 string

Note
<T> 其實是一個型別參數並沒有特定的名稱,你可以使用任何名稱,例如:<Type><U><V><G> 等等。

有點難懂嗎?沒關係,這次我們搭配圖片來理解,首先當我們撰寫 <T> 之後,它在呼叫時將傳入的型別往後傳遞

TypeScript Generics

我們前面 const result = sayHello('Ray'); 傳入的是一個字串,那麼 TypeScript 就會自動推斷 T 的型別為 string

TypeScript Generics

我們可以看到上方圖解 <T> 被替換成了 string,接著這個 string 就會被傳遞到後面的 name 與回傳值,這樣就可以讓 TypeScript 自動推斷型別

TypeScript Generics

而上方這種直接呼叫 sayHello 函式並傳入字串 'Ray' 的方式,稱之為「明確型別」,也就是 TypeScript 可以自動推斷型別,這樣就可以讓 TypeScript 保持型別檢查的功能。

但如果你想要自己手動指定型別,也是可以的,只要在呼叫 sayHello 函式時,指定型別即可

1
const result = sayHello<number>('Ray');

當然泛型不只侷限於函式,你也可以用於介面、類別

1
2
3
4
5
6
7
interface Response<T> {
data: T;
}

const response: Response<string> = {
data: 'Hello, World',
};
1
2
3
4
5
6
7
8
9
10
11
class Box<T> {
private value: T;

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

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

透過泛型,我們就可以避免使用 any 型別,讓 TypeScript 保持型別檢查的功能,這樣就可以讓程式碼更加安全且可讀。

所以最後我們簡單結論一下,其實泛型的概念是將「型別參數化」,另一種理解可以理解成「型別延後指定」的一種方式哩~

關於 Vue 泛型

Vue 在 3.3 之後針對 SFC(Single File Component) 的 <script setup> 提供了泛型的支援,早期 Vue 其實並不容易使用 TypeScript,但是在 3.3 之後,Vue 提供了更多的 TypeScript 支援,

那麼 Vue3 該如何撰寫泛型呢?其實很簡單,在 官方文件 其實是有提供範例的,例如:

1
2
3
4
5
6
7
8
9
10
11
12
<script
setup
lang="ts"
generic="T extends string | number, U extends Item"
>
import type { Item } from './types';

defineProps<{
id: T
list: U[]
}>()
</script>

使用方式其實就是直接在 <script setup> 中加上 generic 屬性,並指定泛型的型別,這樣就可以讓 Vue3 支援泛型了~

但我們可以看到 Vue 中撰寫泛型的方式與一般 TypeScript 有些許不同,所以這邊我也簡單地講解一下上方的程式碼:

  1. T extends string | number:這是指定 T 的型別為 stringnumber
  2. U extends Item:這是指定 U 的型別為 Item,這邊的 Item 是從 ./types 中引入的型別

Vue3 Generics

那麼 , 是在分隔不同的泛型的,所以其實你是可以寫很多個泛型的,例如:T extends string | number, U extends Item, V extends boolean,這樣就可以寫很多個泛型了~

但這邊也為了讓整體更好理解,所以我稍微簡化一下圖片

Vue3 Generics

Vue3 Generics

基本上 extends 是必須的,你可以把它想像成「賦予」的意思,也就是 T 賦予了特定的型別,這樣就可以讓 Vue3 支援泛型哩~

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ