# 🏅 Day 10 - Interface V.S. Type Interface 與 Type 的寫法如下,而功能是都相同的,差別在於 type 需要用 `=` 來宣告,而 `interface` 不用。 ```tsx type Person = { name: string, age: number }; interface Person { name: string, age: number } ``` **Interface 與 Type 的使用時機是何時呢?** 下方用表格來呈現 **`interface`** 和 **`type`** 的一些差異: | 功能/特性 | Interface | Type | | --- | --- | --- | | 可以宣告原始型別 | 否 | 是(例如 type Name = string) | | 描述物件形狀 | 是(核心用途) | 是 | | 擴展性 (繼承/合併) | 是(使用 extends) | 有限(可以通過交集型別 &,但不是真正的繼承) | | 聯合型別 | 否 | 是 | | 元組型別 | 否 | 是 | | 映射型別 | 否 | 是 | | 能作為函式型別 | 是(需使用函式簽名) | 是 | ## type 擴展性實現方式 在 TypeScript 中,雖然 **`type`** 本身無法像 **`interface`** 那樣直接透過 **`extends`** 來進行擴充。 但可以用 **交集型別**(Intersection Types) 來達成類似的效果。 交集型別使用 **`&`** 符號來連接兩個或多個型別。例如,**`Type1 & Type2`** 會建立一個新型別,它包含了 **`Type1`** 和 **`Type2`** 的所有屬性和方法。 如果這些型別中有相同的屬性,這些屬性的型別必須兼容,否則會導致型別錯誤。 ### **交集型別範例** 利用交集型別來建立一個同時包含 **`Person`** 屬性和 **`Employee`** 屬性的新型別: ```tsx= type Person = { name: string; age: number; }; type Employee = { company: string; department: string; }; // 使用交集型別進行合併 type PersonEmployee = Person & Employee; // 使用 PersonEmployee 型別 let employee: PersonEmployee = { name: "Alice", age: 30, company: "Acme Corp", department: "Engineering" }; ``` 在這個範例中,**`PersonEmployee`** 型別結合了 **`Person`** 和 **`Employee`** 的屬性,因此任何符合 **`PersonEmployee`** 型別的物件都必須包含 **`name`**、**`age`**、**`company`** 和 **`department`** 這些屬性。 ### interface 範例 接下來我們試著用 interface 來實現功能: ```tsx // 定義 Person 介面 interface Person { name: string; age: number; } // 定義 Employee 介面,並擴展 Person 介面 interface Employee extends Person { company: string; department: string; } // 使用 Employee 介面 let employee: Employee = { name: "Alice", age: 30, company: "Acme Corp", department: "Engineering" }; console.log(employee); ``` 從上述的例子可以看到,**`interface`** 可以透過 **`extends`** 關鍵字被擴充,讓 **`interface`** 在定義類型或物件時更靈活,特別是在大型專案或複雜的體系當中。 而 type 則是以 **`|`** 和 **`&`** 來建立聯合或交叉型別。雖然 **`interface`** 也可以透過繼承實現類似的 功能,但 **`type`** 在表達聯合類型時更為直接和靈活。 因此在實務層面上可以根據實際遇到的情境、需求來挑選相對適合的表達方式,或許更多時候選擇適來自於個人的偏好、使用習慣或是團隊的 coding style 等等,不過最終使用 type 或 interface 都不會對功能產生不一樣的影響。 ## 單選題 1. 下列何者在 type 與 interface 之間都可以表達且能夠維持相同不喪失資訊? A. Tuple(如 **`[number, string]`**) B. 函式型別(如 **`(x: number) => string`**) C. 具名聯合(如 **`A | B`**) D. 帶有條件型別的工具型別(如 **`T extends U ? X : Y`**) 2. 想要擴充第三方套件的 **`User`** 結構(在不修改原始碼、且維持相容性的前提下)。下列何者最為合適? A. 使用 **`type User = User & { newField: string }`** B. 使用與原本同名的 **`interface User { newField: string }`** C. 使用 **`class User { newField: string }`** 覆寫 D. 使用 **`enum User { newField }`** 3. 以下哪段程式碼「會編譯錯誤」? ```tsx A. type Pair = [number, number]; B. interface action { (x: number): string } C. interface X = string | number; D. type Shape = { r: number } | { w: number; h: number }; ``` ## 實作題 ### 租車費率試算系統 你準備要花大錢創立一個租車服務的事業,而現在你正在為費率計算的設計苦惱。為了透明化租車的價格和估算時間,你希望可以讓使用者可以在 app 上進行試算,在指定好起點和目的地以及車種後,可以大概掌握這趟旅程需要花費多少錢以及花費多少時間,於是你開始著手設計這套系統。 #### 任務描述 1. 定義型別、介面 - 定義一個型別 **`Rate`**,包含基本費率(**`base`**)和每公里的費率(**`perKm`**)。 - 定義一個型別 **`Ride`** 擴充 **`Rate`** 型別,並加上 **`avgSpeedKmH`** 屬性代表平均行駛速度。 - 建立 **`car`**、**`scooter`** 和 **`bike`** 三個 **`Ride`** 型別的變數代表三種交通工具。 - 定義型別 **`Coord`**,包含經緯度資訊,再定義 **`Route`** 型別來描述起點、終點的經緯度資訊。 2. 功能實作 - 設計一個函式 **`distanceKm`**,可以輸入一個 **`Route`** 型別的變數,根據起點、終點的經緯度來計算出距離(使用平面兩點距離公式來計算)。 $$ d = \sqrt{(x_2-x_1)^2+(y_2-y_1)^2} $$ - 設計一個函式 **`calculateQuoteAndTime`**,輸入一個 **`Ride`** 型別變數和一個 **`Route`** 型別變數,以此來計算出旅程的預估時間和金額。 #### 部分程式碼 ```tsx // TODO: 定義 Rate 型別 type Rate // TODO: 定義 Ride 介面擴充 Rate 型別並加上 avgSpeedKmH 屬性 interface Ride{} // 定義座標和路線型別 type Coord = [x: number, y: number]; type Route = [start: Coord, end: Coord]; // 功能實作 // 設計計算距離函式 function distanceKm([start, end]: Route){ const [x1, y1] = start; const [x2, y2] = end; const dx = x2 - x1; const dy = y2 - y1; const km = Math.sqrt(dx * dx + dy * dy); return Number(km.toFixed(2)); } function calculateQuoteAndTime(ride: Ride, route: Route){ // TODO: 計算預估金額和時間 console.log(`預估金額 ${金額} 元,預估時間 ${時間} 分鐘`) } // 定義三種交通工具 const car: Ride = {} const scooter: Ride = {} const bike: Ride = {} // 試算操作 const home: Coord = [25.18, 125.29] const store: Coord = [25.01, 125.10] const school: Coord = [25.36, 125.79] calculateQuoteAndTime(car, [home, store]) calculateQuoteAndTime(bike, [store, school]) ``` <!-- 解答 單選題:B、B、C 實作題: https://codepen.io/zeoxer/pen/emJayqq -->