---
# System prepended metadata

title: "\U0001F3C5 Day 13 - type 泛型"
tags: [TypeScript 30 天證書培訓營 - 2024]

---

#  🏅 Day 13 - type 泛型

泛型也很常用在 type 型別別名上，如以下範例：


```tsx=

// 代入 Passthrough<T> 裡面的 T 為什麼樣的型別
// 賦值後的 T 就會成為該型別
type Passthrough<T> = T;

// 使用 Passthrough 定義不同型別的變數
let numberValue: Passthrough<number> = 42;

// 引發錯誤，型別衝突
let numberValue2: Passthrough<number> = "42";  

let stringValue: Passthrough<string> = "Hello, world!";
let booleanValue: Passthrough<boolean> = true;
let arrayValue: Passthrough<number[]> = [1, 2, 3, 4, 5];

// 使用這些變數
console.log(numberValue); // 輸出: 42
console.log(stringValue); // 輸出: "Hello, world!"
console.log(booleanValue); // 輸出: true
console.log(arrayValue);   // 輸出: [1, 2, 3, 4, 5]

```

當使用型別別名泛型時，TypeScript 不會自動去做型別推斷。所以需要在使用時，明確指定型別參數，例如： **`<number>`** 或 **`<string>`**。


## **情境一：身高範例**

通常情況下，我們可能會這樣定義一個表示人的型別，其中包含身高屬性，並設定為 **`number`** 型別：

```tsx=
type Person = {
  height: number;
};

const mike: Person = {
  height: 180,
};

```

以這例子中，**`mike`** 的身高被設定為一個數字 180。

### **使用泛型的時機**

有可能在專案中，會同時需要用數字，或中文字例如「一百八十公分」來呈現，此時就需要比較靈活的型別定義。這樣泛型就派上用場了。

我們可以這樣定義一個泛型型別名：

```tsx=
type FlexiblePerson<T> = {
  height: T;
};

const mike1: FlexiblePerson<number> = {
  height: 180,
};

const mike2: FlexiblePerson<string> = {
  height: '一百八十公分',
};

```

在 **`mike1`** 的例子中，身高是以數字形式表示的，而在 **`mike2`** 中，身高則是以字串來呈現。


## 情境二：水果物件表

以下是設計一個儲存商品列表資訊的功能。想要用 **`GenericValObj`** 來建立一個物件，該物件將以商品名稱作為 `key`，並以商品價格作為值。

```tsx=
// 新增泛型物件型別
// 屬性 key 必須為字串形式
type GenericValObj<T> = {
    [key: string]: T;
}

// 定義 productPrices 儲存商品價格，value 為 number 型別
const productPrices: GenericValObj<number> = {
    "apple": 1.99,
    "banana": 0.99,
    "cherry": 2.49,
    
};

// 新增更多商品價格
productPrices["grape"] = 1.79;
// productPrices["orange"] = "三美元"; // 會出錯，因為泛型設定的型別參數為 number

// 定義一個 productDescriptions 物件來儲存商品描述，value 為 string 型別
const productDescriptions: GenericValObj<string> = {
    "apple": "一個美味的水果",
    "banana": "一個黃色的水果",
    "cherry": "一個比較小又紅的水果",
};

// 新增更多商品描述
productDescriptions["grape"] = "A small purple fruit";


```



## **情境三：API  response 包裝**

如果你正在開發一個應用程式，需要與多個不同的 API 進行交互。這些 API 回傳不同型別的數據

例如：

1. 一些回傳數字類型的數據（如使用者的年齡）
2. 另一些則回傳字符串類型的數據（如使用者的姓名）

如果想要用統一方法來包裝這些 API response，可以這樣寫：
```tsx
type ApiResponse<T> = {
    data: T;
    status: string;
}

// 使用範例
const ageResponse: ApiResponse<number> = { data: 30, status: "success" };
const nameResponse: ApiResponse<string> = { data: "Alice", status: "success" };

```

如果資料會需要管理 response 回傳成功或失敗的話，可以這樣寫：

```tsx=
// 定義 User 介面
interface User {
    name: string;
    age: number;
}

// 定義 ApiResponse 泛型型別
type ApiResponse<T> = {
    success: boolean;
    message: string | null;
    data: T | null;
}

// 使用 User 介面的 ApiResponse
const userResponse: ApiResponse<User> = {
    success: true,
    message: null,
    data: { name: "Alice", age: 30 }
};

// 用於失敗的 ApiResponse，沒資料
const errorResponse: ApiResponse<null> = {
    success: false,
    message: "Error retrieving data",
    data: null
};


```


## **情境四：網頁版本的 MP3 播放器**

假設你正在開發一個網頁版的 MP3 播放器。這個播放器需要許多不同的設定選項，例如音量控制、播放模式（例如隨機播放）、使用者設定等。

來試著用泛型來進行規劃：

```tsx=
// 定義設定選項的 type 泛型
type ConfigOption<T> = {
    key: string;
    value: T;
    description: string;
    category: string;
}

// 使用範例
const volumeConfig: ConfigOption<number> = {
    key: "volume",
    value: 75,
    description: "控制播放器的音量大小",
    category: "音效設定"
};

const shuffleModeConfig: ConfigOption<boolean> = {
    key: "shuffleMode",
    value: true,
    description: "啟用/停用隨機播放模式",
    category: "播放控制"
};

const usernameConfig: ConfigOption<string> = {
    key: "username",
    value: "user123",
    description: "用戶的顯示名稱",
    category: "用戶設定"
};

```


## 開發題

### 題目一： 資料庫重構優化

請嘗試將
`UserEntry` 和 `ProductEntry`重構，合併成一個自訂的 `type`，並使用泛型來進行優化

```tsx=
// 定義 UserEntry 和 ProductEntry 型別
type UserEntry = {
    id: string;
    data: {
        name: string;
        age: number;
    };
}

type ProductEntry = {
    id: string;
    data: {
        name: string;
        price: number;
    };
}

// 建立 UserEntry 和 ProductEntry 的函式
function createUserEntry(id: string, data: { name: string; age: number; }): UserEntry {
    return { id, data };
}

function createProductEntry(id: string, data: { name: string; price: number; }): ProductEntry {
    return { id, data };
}

// 使用函式來建立 UserEntry 和 ProductEntry
const userEntry = createUserEntry("user1", { name: "John Doe", age: 30 });
const productEntry = createProductEntry("product1", { name: "Apple iPhone 13", price: 799 });
```


### 題目二：旅館訂房網

請嘗試將
`RoomOperationResult` 和 `CustomerOperationResult`重構，合併成一個自訂的`type` ，並使用泛型來進行優化

```tsx=
// 定義 Room 型別別名
type Room = {
    id: string;
    type: string;
    price: number;
    available: boolean;
}

// 定義 Customer 型別別名
type Customer = {
    id: string;
    name: string;
}

// 定義 RoomOperationResult 和 CustomerOperationResult 型別別名
type RoomOperationResult = {
    success: boolean;
    data: Room;
    message: string;
}

type CustomerOperationResult = {
    success: boolean;
    data: Customer;
    message: string;
}

// 建立一個 Customer 陣列來儲存客戶資訊
let customers: Customer[] = [];

// 定義一個函式，該函式接收一個 Room 並回傳一個 RoomOperationResult
function bookRoom(room: Room): RoomOperationResult {
    // 檢查房間是否可用
    if (room.available) {
        // 預訂房間，將 available 屬性設為 false
        room.available = false;

        const result: RoomOperationResult = {
            success: true,
            data: room,
            message: '預訂成功'
        };

        return result;
    } else {
        const result: RoomOperationResult = {
            success: false,
            data: room,
            message: '房間已被預訂'
        };

        return result;
    }
}

// 定義一個函式，該函式接收一個 Room 並回傳一個 RoomOperationResult
function cancelRoom(room: Room): RoomOperationResult {
    // 取消房間，將 available 屬性設為 true
    room.available = true;

    const result: RoomOperationResult = {
        success: true,
        data: room,
        message: '取消預訂成功'
    };

    return result;
}

// 定義一個函式，該函式接收一個 Customer 並回傳一個 CustomerOperationResult
function addCustomer(customer: Customer): CustomerOperationResult {
    // 將客戶添加到客戶陣列中
    customers.push(customer);

    const result: CustomerOperationResult = {
        success: true,
        data: customer,
        message: '新增客戶成功'
    };

    return result;
}

// 建立一個 Room
const room: Room = { id: '1', type: '單人房', price: 1000, available: true };

// 使用 bookRoom 函式來預訂房間
const bookResult = bookRoom(room);
console.log(bookResult); // 輸出：{ success: true, data: { id: '1', type: '單人房', price: 1000, available: false }, message: '預訂成功' }

// 使用 cancelRoom 函式來取消房間
const cancelResult = cancelRoom(room);
console.log(cancelResult); // 輸出：{ success: true, data: { id: '1', type: '單人房', price: 1000, available: true }, message: '取消預訂成功' }

// 建立一個 Customer
const customer: Customer = { id: '1', name: 'John Doe' };

// 使用 addCustomer 函式來添加客戶
const addCustomerResult = addCustomer(customer);
console.log(addCustomerResult); // 輸出：{ success: true, data: { id: '1', name: 'John Doe' }, message: '新增客戶成功' }

console.log(customers); // 輸出：[{ id: '1', name: 'John Doe' }]
```



## 回報流程

將答案寫在 CodePen，並貼至底下回報就算完成了喔! 
解答位置請參考下圖（需打開程式碼的部分觀看）

![](https://i.imgur.com/vftL5i0.png)




<!-- 解答：  



-->




回報區
---


|    Discord    |                          CodePen / 答案                          |
|:-------------:|:----------------------------------------------------------------:|
|洧杰|[Codepen](https://codepen.io/hexschool/pen/poYgYqW?editors=1010)|
|苡安|[Codepen](https://codepen.io/yi-an-yang/pen/ExMvMNQ)|
|展誠|[CodePen](https://codepen.io/hedgehogkucc/pen/LYajavv?editors=1010)|
|hannahpun|[CodePen](https://codepen.io/hannahpun/pen/abMyxWg?editors=0011)|
|Andy|[CodePen](https://codepen.io/qdandy38/pen/BabdEdd?editors=0012)|
|clairechang|[Notion](https://claire-chang.notion.site/Day-13-type-5084ef5556b14f1aa968208ded11c9f6)|
|HsienLu|[CodePen](https://codepen.io/Hsienlu/pen/RwdZVQB)|
|Starr|[CodePen](https://codepen.io/StarrZhong/pen/XWGaLyY)|
|hiYifang|[HackMD](https://hackmd.io/@gPeowpvtQX2Om6AmD-s3xw/r1dPCijta)|
|LinaChen|[HackMD](https://codepen.io/LinaChen/pen/GRevexR)|
|雙魚|[CodePen](https://codepen.io/emiarcak/pen/abMLbxQ?editors=1012)|
|Henrt_Wu|[Codepen](https://codepen.io/hekman1122/pen/RwdLNoa)|
|Alyce|[Codepen](https://codepen.io/alycehwy/pen/oNVGNRR?editors=0010)|
|Mi|[Codepen](https://codepen.io/Mi-Jou-Hsieh/pen/rNRGNKJ?editors=0011)|
|hannahTW|[Codepen](https://codepen.io/hangineer/pen/LYajzzz?editors=0011)|
|jasperlu005|[Codepen](https://codepen.io/uzzakuyr-the-reactor/pen/OJqxMbW?editors=1011)|
|Bryan Chu|[CodePen](https://codepen.io/bryanchu10/pen/NWJaNao)|
|Otis|[CodePen](https://codepen.io/humming74/pen/oNVGzZN?editors=1011)|
|精靈|[CodePen](https://codepen.io/justafairy/pen/xxBXExM)|
|YC|[HackMD](https://hackmd.io/SKoJd3EsTlitnjzCx4Rarg)|
|77_0411|[CodePen](https://codepen.io/chung-chi/pen/XWGezMa?editors=0011)|
|JC|[Codepen](https://codepen.io/jcsamoyed/pen/RwdLxNN?editors=0012)
|Kai|[Codepen](https://codepen.io/kaiyuncheng-the-styleful/pen/ZEPXrbd?editors=0011)
|deedee1215|[Codepen](https://codepen.io/diddy032/pen/rNRGpEE)|
|wendy_.li|[HACKMD](https://hackmd.io/PcmFgqZwRd-4Ep3-LgK5_Q)
|yunhung|[Codepen](https://codepen.io/ahung888/pen/vYPebBg?editors=0011)
|Amberhh| [codepen](https://codepen.io/Amberhh/pen/mdoqerQ?editors=0011)|
|銀光菇| [codepen](https://codepen.io/genesynthesis/pen/eYXeZMY)|
|wei|[codePen](https://codepen.io/jweeei/pen/poYWObp?editors=1012)|
|Lisa|[codePen](https://codepen.io/lisaha/pen/YzgEvyW?editors=1011)|
|翰毅|[codePen](https://codepen.io/yzuigtdw-the-animator/pen/NWJwrYq)|
|rikku1756|[CodePen](https://codepen.io/rikkubook/pen/MWxrpLV?editors=1112)|
|神奇海螺| [CodePen](https://codepen.io/ksz54213/pen/GReyGWr)|
|連小艾|[CodePen](https://codepen.io/bolaslien/pen/YzgYoPQ?editors=0012)|
|BonnieChan|[CodePen](https://codepen.io/Bonnie-chan-the-bold/pen/OJqQJgo?editors=0012)|
|erwin阿瀚|[CodePen](https://codepen.io/yohey03518/pen/wvOymzx)|
|Snorlax|[HackMD](https://hackmd.io/@snorlaxpock/SkMBjXK9T)|
|薏慈|[CodePen](https://codepen.io/its_wang/pen/wvOjZQb)|
|皓皓|[HackMD](https://hackmd.io/@cutecat8110/BJEWBt0F6)|
|leave3310|[HackMD](https://codepen.io/leave3310-the-looper/pen/rNRqeGx?editors=0010)|
|Tori|[HackMD](https://hackmd.io/OAdkiOH-S_WkD-LF6IONVA?view)|
|我是泇吟|[CodePen](https://codepen.io/kljuqbxs/pen/poXmqJm)|


