---
# System prepended metadata

title: "\U0001F3C5 Day 9 - interfaces 延伸功能介紹"
tags: [TypeScript 30 天證書培訓營 - 2024]

---

# 🏅 Day 9 - interfaces 延伸功能介紹

明天我們將要來介紹 `type` 與 `interface` 的使用差異，在那之前我們再來好好將 `interface` 的一些常用功能來進行介紹 :D

## 介面擴展（Interface Extending）

介面有一個 extend 功能來達成介面擴展。可以允許一個介面繼承另一個介面的屬性和方法。這是物件導向程式設計中常見的繼承概念的一種實現。

當你想要建立一個新的介面，並且這個新介面要包含某個已存在介面的所有特性時，他就能派上用場！

### **使用情境一：應用程式帳號權限管理**

1. 一個應用程式需要定義不同種類的使用者。
2. 每個使用者都有一些共通的屬性，如姓名和電子郵件地址。但是，根據使用者的類型（如管理員、訪客等），他們還會有一些額外的特定屬性。
3. 在這情境中，我們可以用一個基本的 **`User`** 介面，然後讓其他更具體的介面繼承這個 **`User`** 介面。


#### **範例程式碼**

```tsx=
// 基本的 User 介面
interface User {
  name: string;
  email: string;
}

// 管理員介面，擴展 User 介面，使用 isAdmin 屬性
interface Admin extends User {
  isAdmin: boolean;
}

// 訪客介面，擴展 User 介面
interface Guest extends User {
  guestSince: Date;
}

// 使用 Admin 介面
const adminUser: Admin = {
  name: "Alice",
  email: "alice@example.com",
  isAdmin: true
};

// 使用 Guest 介面
const guestUser: Guest = {
  name: "Bob",
  email: "bob@example.com",
  guestSince: new Date()
};

```

1. 在這個情境，**`Admin`** 和 **`Guest`** 都擴展了 **`User`** 介面。
2. 代表它們都繼承了 **`User`** 的 **`name`** 和 **`email`** 屬性
3. 並且添加了它們自己的特定屬性：**`Admin`** 有一個 **`isAdmin`** 屬性，而 **`Guest`** 有一個 **`guestSince`** 屬性。
4. 這樣我們就可以根據不同的使用者類型，來建立具有特定屬性的物件了!


### 使用情境二：車輛規格管理

以車輛來說，每台車都有一些共通的屬性，例如品牌和製造年份。

但具備的功能可能會不同，例如有些是吃汽油、有些是電動汽車。

根據車輛的類型，它們還會有一些額外的特定屬性。我們可以使用一個基本的 Vehicle 介面，然後讓其他更具體的介面繼承這個 Vehicle 介面。

#### 基本車輛介面
- 所有車輛共有的屬性：品牌（brand）、製造年份（year）。
#### 汽車介面
- 特定屬性：座位數（seats）、車型（type）。
#### 卡車介面
- 特定屬性：載重量（loadCapacity）。

#### 電動汽車介面
- 特定屬性：電池容量（batteryCapacity）、續航里程（range）。

### **範例程式碼**

```tsx=
// 基本車輛介面
interface Vehicle {
  brand: string;
  year: number;
}

// 汽車介面
interface Car extends Vehicle {
  seats: number;
  type: string;
}

// 卡車介面
interface Truck extends Vehicle {
  loadCapacity: number;
}

// 電動汽車介面
interface ElectricCar extends Vehicle {
  batteryCapacity: number;
  range: number;
}

// 使用 Car 介面
const car: Car = {
  brand: "Toyota",
  year: 2020,
  seats: 5,
  type: "Sedan"
};

// 使用 Truck 介面
const truck: Truck = {
  brand: "Ford",
  year: 2018,
  loadCapacity: 2000
};

// 使用 ElectricCar 介面
const electricCar: ElectricCar = {
  brand: "Tesla",
  year: 2021,
  batteryCapacity: 75,
  range: 300
};

```

1. **`Car`**、**`Truck`** 和 **`ElectricCar`** 介面都擴展了 **`Vehicle`** 介面
2. 讓它們能夠繼承 **`brand`** 和 **`year`** 屬性，同時也各自增加了特定於它們類型的屬性。
3. 除了讓程式更具可讀性和可維護性，同時也保證了資料結構的一致性 :D


## 介面合併（Interface merging）

介面合併允許開發者將**同名的介面宣告合併成一個介面**。這個特性在處理來自多個地方的介面擴展時特別有用

在添加額外屬性到現有介面或在使用第三方套件時很有幫助！

先來上個範例程式碼，主要是定義各種數位產品的特性：

```tsx=
// 初始 DigitalDevice 介面定義了產品的價格
interface DigitalDevice {
    price: number;
}

// 擴展 DigitalDevice 介面以包含產品的重量
interface DigitalDevice {
    weight: number;
}

/*
 介面中的屬性在合併時會簡單的合併到一個介面中，相當於：
  interface DigitalDevice {
   price: number;
   weight: number;
  }
*/

// 使用合併後的 DigitalDevice 介面
const myDevice: DigitalDevice = {
    price: 299.99,
    weight: 1.5
};

// 函式來演示如何使用 DigitalDevice
function displayDeviceDetails(device: DigitalDevice) {
    console.log(`載具價格: ${device.price}, 載具重量: ${device.weight} kg`);
}

// 顯示產品細節
displayDeviceDetails(myDevice);

```

接著我們再透過較常見的情境來進行講解~

### **使用情境：擴展第三方套件型別**

假使你正在使用一個第三方的 UI 套件，這個套件提供一個 **`Button`** 介面來描述按鈕屬性。

假使你想要替這個 **`Button`** 介面添加一些自定義屬性，例如 **`dataTestId`** 用來測試的一個特殊屬性。

以下是如何在 **`app.ts`** 中擴展 **`button.ts`** 中定義的 **`Button`** 介面的範例：

#### **button.ts**

```tsx=
// 第三方套件或另一個檔案中的 Button 介面
export interface Button {
  text: string;
  onClick: () => void;
}

```
#### **extendedButton.ts（你的應用程式想客製化的功能）**

```tsx=
// 引入原始的 Button 介面
import { Button } from './button';

// 在你的應用程式中擴展 Button 介面
declare module './button' {
  interface Button {
    dataTestId?: string;
  }
}
```

#### **app.ts**

```tsx
// 載入第三方按鈕套件
import { Button } from './button';

// 在 app.ts 中擴展 Button 介面
import './extendedButton';


// 現在 Button 介面就有包含 text, onClick 和 dataTestId 屬性嘍
const myButton: Button = {
  text: "Click Me",
  onClick: () => console.log("Button clicked"),
  dataTestId: "my-test-id"
};

// 函式來演示如何使用 Button
function renderButton(button: Button) {
  // 模擬渲染，僅印出 console
  console.log(`顯示按鈕 - 文字: ${button.text}, 資料測試 ID: ${button.dataTestId}`);
}

// 渲染按鈕
renderButton(myButton);

```

這種方法的優點是，**不需要在原始的介面定義檔案中進行任何更改，就可以靈活擴展介面** :D

同時再搭配後面課程提及的 `ES Module、Namespaces、Import` 會更加如虎添翼~



## 單選題
1. 哪一個關鍵字用於 TypeScript 中的介面擴展？
A. **`merge`**
B. **`extend`**
C. **`implements`**
D. **`combine`**

2. 當兩個擁有相同名稱的介面出現在同一作用域中時，TypeScript 會怎麼做？
A. 產生錯誤
B. 忽略其中一個
C. 合併它們
D. 重命名它們

3. 在 TypeScript 中，介面合併（Interface Merging）通常用於什麼情況？
A. 當需要重新命名 interface 時
B. 當需要向現有介面添加更多屬性
C. 當需要創建全新的介面

4. 下面哪種說法正確描述了 TypeScript 中的介面擴展（Interface Extending）？
A. 它允許一個介面從另一個介面繼承屬性和方法。
B. 它允許合併兩個具有不同名稱的介面。


## 回報流程

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

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




<!-- 解答：  


單選題目：B、C、B、A


-->




回報區
---


|    Discord    |                          CodePen / 答案                          |
|:-------------:|:----------------------------------------------------------------:|
|洧杰|[Codepen](https://codepen.io/hexschool/pen/poYgYqW?editors=1010)|
|苡安|[Codepen](https://codepen.io/yi-an-yang/pen/QWopzNo)|
|YC|[HackMD](https://hackmd.io/SKoJd3EsTlitnjzCx4Rarg)|
|BonnieChan|[Codepen](https://codepen.io/Bonnie-chan-the-bold/pen/poYeqwz?editors=1000)|
|神奇海螺|[CodePen](https://codepen.io/ksz54213/pen/qBvrLxX)|
|HsienLu|[CodePen](https://codepen.io/Hsienlu/pen/eYXvbMZ)|
|銀光菇|[CodePen](https://codepen.io/genesynthesis/pen/OJqpraq)|
|ZS|[CodePen](https://codepen.io/irishuang/pen/oNVZJmq?editors=0010)|
|Amberhh| [codepen](https://codepen.io/Amberhh/pen/eYXvxOv)|
|展誠|[Codepen](https://codepen.io/hedgehogkucc/pen/XWGMOma?editors=1010)|
|Kai|[Codepen](https://codepen.io/kaiyuncheng-the-styleful/pen/WNmpPJo)|
|LinaChen|[Codepen](https://codepen.io/LinaChen/pen/ExMWrGe)|
|rikku1756|[Codepen](https://codepen.io/rikkubook/pen/poYeYKx?editors=1010)|
|77_0411|[Codepen](https://codepen.io/chung-chi/pen/LYaWvEb?editors=1000)|
|Mi|[CodePen](https://codepen.io/Mi-Jou-Hsieh/pen/MWxpboE)|
|Jack|[CodePen](https://codepen.io/lj787448952/pen/OJqpGqG)|
|hiYifang|[HackMD](https://hackmd.io/@gPeowpvtQX2Om6AmD-s3xw/SkhePfMKa)|
|m_m|[CodePen](https://codepen.io/minnn7716/pen/gOERNYV)|
|clairechang|[Notion](https://claire-chang.notion.site/Day-9-interfaces-ad477ae2cdd5466fa92ab94c6d379647)|
|Bryan Chu|[Notion](https://codepen.io/bryanchu10/pen/BabRBeL)|
|Henry_Wu|[Codepen](https://codepen.io/hekman1122/pen/VwRbwWv?editors=1010)|
|wendy_.li|[HACKMD](https://hackmd.io/PcmFgqZwRd-4Ep3-LgK5_Q)
|Zuo|[Codepen](https://codepen.io/linchinhsuan/pen/ExMmjBd?editors=1010)
|hannahTW|[Codepen](https://codepen.io/hangineer/pen/oNVWgOE)
|jasperlu005|[Codepen](https://codepen.io/uzzakuyr-the-reactor/pen/yLwbYqQ)|
|NiuNiu|[Stackblitz](https://stackblitz.com/edit/typescript-sathex?file=index.ts,index.html)|
|Mia Tsai|[CodePen](https://codepen.io/Mianzi/pen/gOEWPav)|
|deedee1215|[CodePen](https://codepen.io/diddy032/pen/GRempYm)|
|Otis|[CodePen](https://codepen.io/humming74/pen/gOEWPWz)|
|yunhung|[CodePen](https://codepen.io/ahung888/pen/WNmjxxE)|
|Teddy|[CodePen](https://codepen.io/TaideLi/pen/MWxmemB)|
|Starr|[CodePen](https://codepen.io/StarrZhong/pen/qBvmqZo)|
|Lisa|[CodePen](https://codepen.io/lisaha/pen/mdomZMV)|
|erwin阿瀚|[CodePen](https://codepen.io/yohey03518/pen/wvOewmv)
|薏慈|[CodePen](https://codepen.io/its_wang/pen/oNVwGMM)
|連小艾|[Codepen](https://codepen.io/bolaslien/pen/wvOeXvR?editors=1010)|
|Alyce|[Codepen](https://codepen.io/alycehwy/pen/wvOePEd?editors=0010)|
|皓皓|[HackMD](https://hackmd.io/@cutecat8110/Hyj_s_iYT)|
|精靈|[CodePen](https://codepen.io/justafairy/pen/JjzJmYN)|
|JC|[Codepen](https://codepen.io/jcsamoyed/pen/zYbzMee?editors=0010)|
|shan13|[Codepen](https://codepen.io/yishan13-tsai/pen/rNRwbOR)|
|翰毅|[CodePen](https://codepen.io/yzuigtdw-the-animator/pen/YzgxNXB)|
|Snorlax|[HackHD](https://hackmd.io/@snorlaxpock/HJRcf3jYT)|
|Rochel|[Codepen](https://codepen.io/rochelwang1205/pen/qBvozRd)|
|leave3310|[Codepen](https://codepen.io/leave3310-the-looper/pen/eYXKpww?editors=0010)|
|Nick Lin|[Codepen](https://codepen.io/NickLinP/pen/xxBQPqX)|
|我是泇吟|[Codepen](https://codepen.io/kljuqbxs/pen/vYqwWOL)|