---
# System prepended metadata

title: "\U0001F3C5 Day 11 - 什麼是泛型？"
tags: [TypeScript 直播班-2025]

---

# 🏅 Day 11 - 什麼是泛型？

## 回顧陣列設定型別的方式

例如：
1. `number[]` 表示一個數字型別陣列
2. `string[]` 表示一個字串型別的陣列。

```=tsx
let numbers: number[] = [1, 2, 3];
let strings: string[] = ["hello", "world"];
```

除此之外，還有哪些定義方式呢？

## 陣列泛型（Array Generic）

此時也可以使用陣列泛型 `Array<元素型別>`，來達成一樣的效果：

```=tsx
let numbers: Array<number> = [1, 2, 3];
let strings: Array<string> = ["hello", "world"];    
```

這兩種方式都有符合一樣的效果，下方我們再寫個範例，主要是提供一個字串陣列，並單純回傳這個字串陣列，兩個效果也是一樣的，只是格式不同而已，一個是 **`Array<string>`** 另一個是 **`string[]`**。 

### 範例一：**`Array<string>`**：

```tsx
function processStringArray1(arg: Array<string>): Array<string> {
  return arg;
}


let stringArray1: Array<string> = ["apple", "banana", "cherry"];
processStringArray1(stringArray1); // 輸出: ["apple", "banana", "cherry"]

```

### 範例二：**`string[]`**：

```tsx
function processStringArray2(arg: string[]): string[] {
  return arg;
}

// 使用這個函式
let stringArray2: string[] = ["apple", "banana", "cherry"];
processStringArray2(stringArray2); // 輸出: ["apple", "banana", "cherry"]

```



## 開始進入泛型（Generics）

泛型（Generics）是一種在定義函式、介面或類別時，不先明確指定具體的型別，而是在實際使用時，再填入型別的特性。

```=tsx
// `T` 是型別參數（Type Parameter），可代進任意輸入的型別。

function hello<T>(data:T){
    console.log(data)
}

hello<string>("Jack")//Jack，data 參數型別會被代入為 String
hello<string>(123) //會出錯，型別錯誤

hello<number>(123)//123，data 參數型別會被代入為 number
```

上面的範例，我在函式名稱 `hello` 後添加了 `<T>`，裡面的 `T` 是**型別參數（Type Parameter）**，可以用來代入任意型別。

這個 `T` 也可以使用其他名稱，例如 `U`，下面程式碼也具備相同功能

```tsx=
             //型別參數改為 U 
function hello<U>(data:U){
    console.log(data)
}

hello<string>("Jack")//Jack，data 參數型別會被代入為 String
hello<string>(123) //會出錯，型別錯誤

hello<number>(123)//123，data 參數型別會被代入為 number
```
不過通常約定俗成主要會使用 `T` 當作型別參數的原因是 `T`  是 type(型別) 縮寫的關係


### 泛型函式 return
當然這個型別參數，也能應用在 `return` 上

```=tsx
                         // return 也用到了型別參數
function hello<T>(data:T): T{
    return data;
}

hello<string>("Jack")//回傳 Jack
hello<string>(123) //會出錯，型別錯誤

hello<number>(123)//回傳 123
```

### 可以有多個型別參數

```tsx=
function callFun<T, U>(e1: T, e2: U): [U, T] {
  return [e2, e1];
}

               // 推薦：可以使用型別推論寫法
const a = callFun("world", 123);
console.log(a); // [123, "world"]

            // 也可以使用型別註釋
const b = callFun<string,number>("world", 123);
console.log(b); // [123, "world"]

           // 故意出錯的型別註釋
const c = callFun<string,string>("world", 123);
console.log(c); // [123, "world"]

```

## 問題來了，如果是這樣呢？

```js=
function processStringArray(strings: string[]): string[] {
    // ... 一些對字串陣列的操作
    return strings;
}

function processNumberArray(numbers: number[]): number[] {
    // ... 一些對數字陣列的操作
    return numbers;
}

```

看起來兩個函式有一個共通之處，那就是函式參數、return 的型別都一樣，有辦法透過泛型優化嗎？
### **重構為泛型函式**

我們可以將這些函式重構為一個泛型函式，能夠接受任何型別的陣列。泛型 **`T`** 代表陣列中的元素型別。



```tsx
function processArray<T>(arr: T[]): T[] {
    return arr;
}

let strAry = ["apple", "banana", "cherry"];
let processedStrings = processArray<string>(strAry);

let numberAry = [1, 2, 3];
let processedNumbers = processArray<number>(numberAry);

```


在這個重構後的範例中，**`processArray`** 函式使用泛型 **`T`** 來表示陣列元素的型別。當你呼叫這個函式時，可以明確指定這個型別，如 **`processArray<string>`** 或 **`processArray<number>`**。

這樣你就能夠用同一個函式來處理多種型別的陣列，藉此來提升代碼的簡潔性。

## 實作題

### 1. 建立泛型字串陣列
#### 函式名稱
`createGenericStringArray`
#### 輸入
兩個字串 `str1` 和 `str2`。
#### 輸出
一個泛型陣列 `Array<string>`，包含 `str1` 和 `str2`。
#### 範例
輸入`"hello"` 和 `"world"`，函式回傳陣列為 `["hello", "world"]`。

#### 部分程式碼
```tsx=
function createGenericStringArray(str1: <設定型別>, str2: <設定型別>): <設定回傳型別> {
    return <回傳格式>;
}

// 使用這個函式
const stringArray = createGenericStringArray("hello", "world");
console.log(stringArray); // 輸出: ["hello", "world"]
```

### **2. 泛型函式**
練習泛型 `<T>` 用法
#### **函式名稱**

**`identity`**

#### **輸入**

一個型別參數 **`value`**。

#### **輸出**

直接回傳輸入的 **`value`**。

#### **範例**

輸入一個數字 **`5`**，函式回傳 **`5`**；輸入一個字串 **`"hello"`**，函式回傳 **`"hello"`**。

#### **部分程式碼**

```tsx=
              // 補上型別參數
function identity(value):? {
    return value;
}

// 使用這個函式
const number = identity(5);
console.log(number); // 輸出: 5

const string = identity("hello");
console.log(string); // 輸出: "hello"
```



### **3. 使用泛型處理不同型別的物件**

#### **函式名稱**

**`processArray`**

#### **輸入**

- 一個泛型陣列 **`array: Array<T>`**，其中 **`T`** 可以是 **`string`**、**`number`** 或 **`boolean`**。
- 一個泛型函式 **`process: (item: T) => T`**，用於對陣列中的每個元素進行處理。

#### **輸出**

一個經過處理的泛型陣列 **`Array<T>`**，包含經過函式 **`process`** 處理過的所有元素。

#### **範例**

1. 輸入字串陣列 **`["apple", "banana"]`** 和一個將每個字串轉為大寫的函式，函式回傳陣列為 **`["APPLE", "BANANA"]`**。
2. 輸入數字陣列 **`[1, 2, 3]`** 和一個將每個數字加倍的函式，函式回傳陣列為 **`[2, 4, 6]`**。
3. 輸入布林陣列 **`[true, false, true]`** 和一個將每個布林值取反的函式，函式回傳陣列為 **`[false, true, false]`**。

#### **部分程式碼**

```tsx=
function processArray(array, process){
    return ???
}

function processString(str: string): string{
  return ???
}

function processNumber(num: number): number {
  return ???
}

function processBoolean(bool: boolean): boolean {
  return ???
}

// 使用這個函式
const processedStrings = processArray(["apple", "banana"], processString);
const proceedingNums = processArray([1, 2, 3], processNumber)
const proceedingBooleans = processArray([true, false, true], processBoolean)
console.log(processedStrings); // 輸出: ["APPLE", "BANANA"]
console.log(proceedingNums); // 輸出: [2, 4, 6]
console.log(proceedingBooleans); // 輸出: [false, true, false]
```




<!-- 解答：  

1. 
function createGenericStringArray(str1: string, str2: string): Array<string> {
    return [str1, str2];
}

2. 
function identity<T>(value: T): T {
    return value;
}

3. 
function processArray<T extends string | number | boolean>(array: T[], process: (i: T) => T): T[] {
    return array.map(item => process(item))
}

function processString(str: string): string{
  return str.toLocaleUpperCase()
}

function processNumber(num: number): number {
  return num * 2
}

function processBoolean(bool: boolean): boolean {
  return !bool
}

const processedStrings = processArray<string>(["apple", "banana"], processString);
const proceedingNums = processArray<number>([1, 2, 3], processNumber)
const proceedingBooleans = processArray<boolean>([true, false, true], processBoolean)

-->