---
# System prepended metadata

title: "\U0001F3C5 Day 10 - Interface V.S. Type"
tags: [TypeScript 直播班-2025]

---

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

-->