--- title: 'Typescript 泛用型別' --- Typescript 泛用型別 === ###### tags: `Typescript` ## Table of Contents [TOC] 泛用型別 X 型別參數化 --- ### 一、泛用型別 原理跟普通的函式概念差不多,可以把泛用型別看成是型別版的函式 ```typescript= type Identuty<T> = T ``` >泛用型別就是將型別化名進行參數化的動作 #### 1. 任何型別化名都可以轉換成泛用型別 - 在泛用型別化名的名稱後面接上的 <> 內容就是型別參數(Type Parameter)的宣告。 - 型別參數可以有複數個,只要在 <> 裡用逗號分隔就可以了。 型別參數可以有複數個,只要在 <> 裡用逗號分隔就可以了。 ![](https://i.imgur.com/xLgNFcQ.png) 在定義 Dictionary 泛型別後,correctDict 變數內的值就必須為布林 ```typescript= type Dictionary<T> = { [prop: string]: T } let correctDict: Dictionary<boolean> = { wentToClub: true, playedMahjong: true, } ``` #### 2. 宣告函式時就使用泛型 ```typescript= function identityFunc<T>(something: T): T { return something } ``` 如果你呼叫該函式時是 identityFunc<string> 這樣呼叫的,輸入必須填數 string 型別,輸出也是 string 型別。 ![](https://i.imgur.com/nGsqX9J.png) #### 3. 將泛型 T 裡面的選用屬性轉成必要屬性 ```typescript= interface PersonalInfo { name: string, age?: number, hasPet?: boolean } let validPerson: PersonalInfo = { name: 'Maxwell', hasPet: true } let incompletePersonalInfo: Required<PersonalInfo> = { name: 'Maxwell', age: 20 } ``` ![](https://i.imgur.com/0BMCq4i.png) 泛型註記 X 推論未來 --- #### 泛用型別的宣告 型別化名分成三種 —— - 型別(Type) ``` type GT<TP1, TP2, TP3, ...,TPn> = (型別化名內容必須包含 TP1, TP2, TP3, ...,TPn) ``` - 介面(Interface) ``` interface GT<TP1, TP2, TP3, ...,TPn> = (型別化名內容必須包含 TP1, TP2, TP3, ...,TPn) ``` - 類別(Class) ``` class GT<TP1, TP2, TP3, ...,TPn> = (型別化名內容必須包含 TP1, TP2, TP3, ...,TPn) ``` - 泛用抽象類別就是在泛用類別旁邊註記 abstract 關鍵字 ``` abstract class GT<TP1, TP2, TP3, ...,TPn> = (型別化名內容必須包含 TP1, TP2, TP3, ...,TPn) ``` #### 預設型別參數 Default Type Parameter 用等號連結型別值 ```typescript= // 預設型別 type DefaultStringDictionary<T = string> = { [prop: string]: T } // 預設鍵值對的值之型別 string let stringDict: DefaultStringDictionary = { message: 'Hello World', language: 'TypeScript' } // 覆蓋鍵值對的值之型別 boolean let booleanDict: DefaultStringDictionary<boolean> = { hasPet: false, hasMotorcycle: true, } ``` #### 泛用型別化名的型別參數限制 Type Constraint in Generic Type Alias 某泛用型別化名 GT 之型別參數 Tparam 被限制在某型別 Tcontraint 範圍之下,可以使用 extends 關鍵字,其格式為: ![](https://i.imgur.com/igKz8KP.png) ```typescript= type Primitives = number | string | boolean | null | undefined type PrimitiveArray<T extends Primitives> = Array<T> let numberPrimitiveArr: PrimitiveArray<number> = [1, 2, 3] let stringPrimitiveArr: PrimitiveArray<string> = ['1', '2', '3'] let numberOrStringPrimitiveArr: PrimitiveArray<number | string> = [1, '2', 3] interface IPersonalInfo { name: string, age: number, hasPet: boolean } // Primitives 中沒有 Object 的 union,TS 會警告錯誤 let invalidObjectArray: PrimitiveArray<IPersonalInfo> = [ { name: 'Maxwell', age: 20, hasPet: false } ] ``` #### 泛用函式 - 泛用函式的宣告 必須要將型別參數馬上宣告在函式名稱的後面。若某函式所擁有的型別參數有 TP1、TP2、...、TPn ```typescript= function F<TP1, TP2, ...,TPn>(/*函數之參數*/): Treturn { /* 函式內容 */ } ``` - 藉由隱藏在陣列裡的泛用機制,編譯器早就可以推論出回呼函式的參數型別。 ```typescript= // 泛用函式 function traverseElements<T>( value: Array<T>, callback: (el: T, index: number) => void ) { for (let i = 0; i < value.length; i += 1) { callback(value[i], i) } } // 呼叫函式 let numberArrayInput = [2, 5, 7, 9] let traverseCallback = function(el: number, index: number) { console.log(`Index ${index} - Value ${el}`); } traverseElements<number>( numberArrayInput, traverseCallback ) // 簡化成 traverseElements<number>( [2, 5, 7, 9], (el, index) => { console.log(`Index ${index} - Value ${el}`); } ) ``` - 泛用型別可以推論未來的型別使用的可能性 下例中的 Array.prototype.map 可以正確地推論出陣列中的型別 ![](https://i.imgur.com/Fkr4QNg.png) 介面與類別 X 泛型註記機制 --- #### 泛用類別 Generic Class ```typescript= class C<T> { constructor(public memberProp: T) {} public memberFunc() { return this.memberProp } get value() { return this.memberProp } set value(input: T) { this.memberProp = input } } ``` - 不註記變數,建構 c 物件時填入型別參數 ![](https://i.imgur.com/t3MQ0rC.png) 泛型別帶入 number,其輸出的成員方法及成員變數也會輸出 number ![](https://i.imgur.com/diNGrTq.png) - 就算沒有對泛型做積極註解,ts 也會根據參數帶入的值去推導出泛型的型別 ![](https://i.imgur.com/fSLyfSh.png) - 泛用型別的繼承 在 class D 的部分,因為不知道<T>的型別,因此要像 class F 一樣給一個型別 ![](https://i.imgur.com/3v4VkbU.png) 迭代器模式 X 泛用迭代器 --- 迭代器是專門巡訪聚合物的一個物件 聚合物指的是:陣列、列狀物、集合(Set)