# 🏅 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,並貼至底下回報就算完成了喔!
解答位置請參考下圖(需打開程式碼的部分觀看)

<!-- 解答:
單選題目: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)|