掌握 TypeScript 泛型(Generics) & Vue3 泛型
前言
泛型(Generics)是 TypeScript 中很重要的特性,它可以大幅度簡化程式碼的撰寫,並提高程式碼的可讀性,但是 TypeScript 的泛型也是初學者最難理解的部分之一,所以就來寫一篇文章來介紹 TypeScript 的泛型。
什麼是泛型?
在說明泛型之前,我們先來看一下基本常見的型別宣告:
1 | const myName: string = 'Ray'; |
上方的例子中,我們宣告了一個常數 myName
,並指定型別為 string
,這有助於我們在開發時,可以透過編輯器的提示來知道 myName
的型別為 string
,這樣就可以避免在開發時發生型別錯誤。
Note
型別錯誤,在此是指 JavaScript 的隱含型別轉型導致的錯誤,例如:1 + '1'
會得到'11'
,這是因為 JavaScript 會將數字1
轉換為字串'1'
,再進行字串相加。
除此之外,我們也可以透過函式來指定型別:
1 | function sayHello(name: string): string { |
上方的程式碼中,意思是指宣告一個函式 sayHello
,並指定參數 name
的型別為 string
,回傳值的型別也為 string
,這樣就可以確保在呼叫 sayHello
函式時,參數 name
的型別為 string
,回傳值的型別也為 string
。
但是有時候我們並不知道參數的型別,你可能會想說就用 any
型別
1 | function sayHello(name: any): any { |
但是這樣的寫法並不安全,因為 any
型別會讓 TypeScript 失去型別檢查的功能,這樣反而會增加程式碼的錯誤率以及失去使用 TypeScript 的意義。
那麼接下來就要來介紹泛型了,泛型是一種讓 TypeScript 可以在不指定型別的情況下,讓 TypeScript 自動推斷型別的一種特性,簡單來講泛型就是「型別參數化」。
什麼意思呢?就是當我們不知道參數的型別時,可以透過泛型來讓 TypeScript 自動推斷型別,這樣就可以避免使用 any
型別,讓 TypeScript 保持型別檢查的功能。
1 | function sayHello<T>(name: T): T { |
使用方式非常簡單,只要在函式名稱後面加上 <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>
之後,它在呼叫時將傳入的型別往後傳遞
我們前面 const result = sayHello('Ray');
傳入的是一個字串,那麼 TypeScript 就會自動推斷 T
的型別為 string
我們可以看到上方圖解 <T>
被替換成了 string
,接著這個 string
就會被傳遞到後面的 name
與回傳值,這樣就可以讓 TypeScript 自動推斷型別
而上方這種直接呼叫 sayHello
函式並傳入字串 'Ray'
的方式,稱之為「明確型別」,也就是 TypeScript 可以自動推斷型別,這樣就可以讓 TypeScript 保持型別檢查的功能。
但如果你想要自己手動指定型別,也是可以的,只要在呼叫 sayHello
函式時,指定型別即可
1 | const result = sayHello<number>('Ray'); |
當然泛型不只侷限於函式,你也可以用於介面、類別
1 | interface Response<T> { |
1 | class Box<T> { |
透過泛型,我們就可以避免使用 any
型別,讓 TypeScript 保持型別檢查的功能,這樣就可以讓程式碼更加安全且可讀。
所以最後我們簡單結論一下,其實泛型的概念是將「型別參數化」,另一種理解可以理解成「型別延後指定」的一種方式哩~
關於 Vue 泛型
Vue 在 3.3 之後針對 SFC(Single File Component) 的 <script setup>
提供了泛型的支援,早期 Vue 其實並不容易使用 TypeScript,但是在 3.3 之後,Vue 提供了更多的 TypeScript 支援,
那麼 Vue3 該如何撰寫泛型呢?其實很簡單,在 官方文件 其實是有提供範例的,例如:
1 | <script |
使用方式其實就是直接在 <script setup>
中加上 generic
屬性,並指定泛型的型別,這樣就可以讓 Vue3 支援泛型了~
但我們可以看到 Vue 中撰寫泛型的方式與一般 TypeScript 有些許不同,所以這邊我也簡單地講解一下上方的程式碼:
T extends string | number
:這是指定T
的型別為string
或number
U extends Item
:這是指定U
的型別為Item
,這邊的Item
是從./types
中引入的型別
那麼 ,
是在分隔不同的泛型的,所以其實你是可以寫很多個泛型的,例如:T extends string | number, U extends Item, V extends boolean
,這樣就可以寫很多個泛型了~
但這邊也為了讓整體更好理解,所以我稍微簡化一下圖片
基本上 extends
是必須的,你可以把它想像成「賦予」的意思,也就是 T
賦予了特定的型別,這樣就可以讓 Vue3 支援泛型哩~