# 🏅 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)|