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