---
# System prepended metadata

title: Typescript 泛型
tags: [typescript]

---

# Typescript 泛型
###### tags: `typescript`

最近發現專案中常常有類別或方法旁邊加上:`<b>`的東西，只知道是泛型卻不明白所以，所以就來了解一下ts中的泛型吧!

## 函式使用泛型
在函式後面加上 <Type> 表示動態型別，<Type> 命名也是可以自行定義的，如<List>。只是 <T> 及 <Type>比較通用，代表著<span style="color:#ea748c">傳進來的型別是甚麼，輸出後的型別他就會是傳進來的型別 </span>，以下是簡單的使用範例
```typescript!
function identity<T>(arg: T): T {
    return arg;
}

// 調用
let output1 = identity<string>("hello");
let output2 = identity<number>(100);

console.log(output1); // 輸出 hello
console.log(output2); // 輸出 100

function identity2<Type>(arg: Type[]): Type[] {
  return arg;
}

let output3 = identity2([1, 2, 3]); 
let output4 = identity2(["a", "b", "c"]);
console.log(output3);// [1, 2, 3]
console.log(output4); //["a","b","c"]
```
調用函式時，可將型別傳入函式，可以指定型別`idebtity2<string>("D","E")`傳入型別為string，也可以將型別判斷交給ts的自動推論(type argument inference)，當然這是最常見的方式了:
```typescript!
let output5 = identity2<string>(["a", "b", "c"]); //指定泛型回傳型別 `<type>`
let output6 = identity2(["a", "b", "c"]); //inference 自動推論
console.log(output5); // [ 'a', 'b', 'c' ]
console.log(output6); // [ 'a', 'b', 'c' ]
```
    
    
### 類別class使用泛型
泛型也可以用於類別的型別定義中：
```typescript!
/** 範例一:: */
class Box<T> {
    private value: T;

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

    getValue(): T {
        return this.value;
    }
}
	
// 使用泛型類
let box1 = new Box<string>("hello");
let box2 = new Box<number>(100);

console.log(box1.getValue()); // 輸出 hello
console.log(box2.getValue()); // 輸出 100
	
	
/** 範例二:: */
class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}

//限制為 number 型別
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};

```


### 使用泛型介面

```typescript!
interface Collection<T> {
    add(item: T): void;
    remove(item: T): void;
    getItems(): T[];
}

class MyCollection<T> implements Collection<T> {
    private items: T[] = [];

    add(item: T): void {
        this.items.push(item);
    }

    remove(item: T): void {
        let index = this.items.indexOf(item);
        if (index !== -1) {
            this.items.splice(index, 1);
        }
    }

    getItems(): T[] {
        return this.items;
    }
}

// 使用泛型接口
let collection1: Collection<number> = new MyCollection<number>();
collection1.add(1);
collection1.add(2);
collection1.add(3);
console.log(collection1.getItems()); // 輸出 [1, 2, 3]

let collection2: Collection<string> = new MyCollection<string>();
collection2.add("hello");
collection2.add("world");
console.log(collection2.getItems()); // 輸出 ["hello", "world"]

```
### 約束泛型
泛型也並非是可以隨意使用的，因為在不了解`<type>`的類型時，依然有可能會有錯誤的情況發生，例如:
```typescript!
function logIdentity<T>(arg: T): T {
    console.log(arg.length);
    return arg;
}

//error:Property 'length' does not exist on type 'T'.
```
因為`<T>`泛型T中不一定包含屬性`length`，所以錯誤了。因此，我們可以對泛型進行約束:
```typescript!
interface Lengthwise {
    length: number;
}

function loggingIdentity2<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity2(3); //error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'. (不符合約束條件interface)
loggingIdentity2({ length: 10, value: 3 });
```
### 在約束泛型中使用類型參數
可以指定受另一個型別約束的參數型別， 如 Key 收到 Type 的型別約束(keyof)， 所以 key 參數型別只能是 obj 參數所定義的型別。

如下面例子 getProperty(x, "m")時， 就會報錯提醒沒有 m 參數。
```typescript!
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
 
let x = { a: 1, b: 2, c: 3, d: 4 };
 
let value1 = getProperty(x, "a");
let value2 = getProperty(x, "m"); //error: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'. m 不存在於 x 當中的key值

console.log(value1); //1
console.log(value2); //undefined
```
  
### 泛型介面 Generic Interface
使用含有泛型的介面來定義函式的形狀:
```typescript!
interface GenericIdentity {
  <Type>(arg: Type): Type
}
 
function identity<Type>(arg: Type): Type {
  return arg;
}
 
let myIdentity: GenericIdentity = identity;
```
可以把泛型引數提前到介面名上：
```typescript!
interface GenericIdentity<T> {
  (arg: T): T;    
}
function identity(arg: T): T {
		return arg;
}
let myIdentity: GenericIdentity<number> = identity;
```
注意: 在使用泛型介面的時候，需要定義泛行的型別。