泛型是一個強大的程式設計概念,它允許我們建立通用的、可重用的程式碼。它的優點不僅在於它可以適用於不同的資料型別,還在於它提供了靜態型別檢查的好處。在 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 就能夠正確理解程式碼並進行型別檢查。
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);
});
我們可以看到 promise
變數實際上是 Promise
型別。當我們看到尖括號時,它表示這個 promise
最終得到的結果的型別資訊。然而,當我們看到 unknown
時,這是因為 TypeScript 不能完全理解我們實際上會分析的是一個字串。
讓我們修改程式碼,現在型別是 Promise
。Promise
就像陣列一樣可以與其他型別一起運作,因為最終它會返回某種型別的資料。在這個範例中,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 方法
});
<T>
來定義泛型型別,其中 T
是型別參數。