---
title: 'Typescript 泛用型別'
---
Typescript 泛用型別
===
###### tags: `Typescript`
## Table of Contents
[TOC]
泛用型別 X 型別參數化
---
### 一、泛用型別
原理跟普通的函式概念差不多,可以把泛用型別看成是型別版的函式
```typescript=
type Identuty<T> = T
```
>泛用型別就是將型別化名進行參數化的動作
#### 1. 任何型別化名都可以轉換成泛用型別
- 在泛用型別化名的名稱後面接上的 <> 內容就是型別參數(Type Parameter)的宣告。
- 型別參數可以有複數個,只要在 <> 裡用逗號分隔就可以了。
型別參數可以有複數個,只要在 <> 裡用逗號分隔就可以了。

在定義 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 型別。

#### 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
}
```

泛型註記 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 關鍵字,其格式為:

```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 可以正確地推論出陣列中的型別

介面與類別 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 物件時填入型別參數

泛型別帶入 number,其輸出的成員方法及成員變數也會輸出 number

- 就算沒有對泛型做積極註解,ts 也會根據參數帶入的值去推導出泛型的型別

- 泛用型別的繼承
在 class D 的部分,因為不知道<T>的型別,因此要像 class F 一樣給一個型別

迭代器模式 X 泛用迭代器
---
迭代器是專門巡訪聚合物的一個物件
聚合物指的是:陣列、列狀物、集合(Set)