測驗:interface 與 type — 定義物件的形狀
共 5 題,點選答案後會立即顯示結果
1. 在 TypeScript 中,interface 的主要用途是什麼?
2. 以下程式碼中,email?: string 的問號 ? 代表什麼意思?
3. 關於 interface 和 type 的差異,以下哪個說法是正確的?
4. 以下程式碼會發生什麼事?
5. 以下程式碼中,Dog 最終包含哪些欄位?
一句話說明
告訴 TypeScript「這個物件裡面要有哪些欄位、什麼型別」。
前置知識
- 本系列第 2 篇:基本型別標註(
string、number、boolean等) - JavaScript 的物件操作(
{ name: "Alice" }這種寫法)
為什麼需要定義物件的形狀?
AI 幫你寫程式時,經常會產生這樣的程式碼:
interface User {
name: string;
age: number;
email?: string;
}
Code language: PHP (php)這不是什麼神秘語法。它只是在說:「User 這個物件,必須有 name(文字)和 age(數字),可以有 email(文字)。」
你不需要會寫這些定義,但你需要看懂它在規定什麼,因為這會決定整個程式裡物件能用哪些欄位。
interface:定義物件結構
最小範例
interface User {
name: string;
age: number;
}
const alice: User = { name: "Alice", age: 25 };
Code language: PHP (php)逐行翻譯
interface User { // 定義一個叫 User 的物件結構
name: string; // 必須有 name,型別是文字
age: number; // 必須有 age,型別是數字
} // 結構定義結束
const alice: User = { // alice 這個變數必須符合 User 的結構
name: "Alice", // 有 name,OK
age: 25, // 有 age,OK
};
Code language: PHP (php)翻译:「定義一個 User 結構,裡面一定要有 name(文字)和 age(數字)。alice 這個物件符合 User 結構。」
如果你少寫一個欄位,TypeScript 會報錯:
const bob: User = { name: "Bob" };
// 錯誤!缺少 age 屬性
Code language: JavaScript (javascript)可選屬性 ?:有也行、沒有也行
最小範例
interface User {
name: string;
age: number;
email?: string; // 加了 ? 就是可選的
}
const alice: User = { name: "Alice", age: 25 }; // OK,沒有 email 也行
const bob: User = { name: "Bob", age: 30, email: "b@x" }; // OK,有 email 也行
Code language: PHP (php)翻譯對照
| 你會看到 | 意思 |
|---|---|
name: string |
必填,一定要有 |
email?: string |
可選,有沒有都行 |
重點:問號 ? 放在屬性名稱後面、冒號前面,代表「這個欄位可以不存在」。
唯讀屬性 readonly:設定後不能改
最小範例
interface User {
readonly id: number;
name: string;
}
const alice: User = { id: 1, name: "Alice" };
alice.name = "Bob"; // OK,name 可以改
alice.id = 2; // 錯誤!id 是唯讀的,不能修改
Code language: PHP (php)翻譯:「readonly 表示這個欄位建立之後就不能再改了。」
常見場景:資料庫的 id、建立時間 createdAt 這類不應該被修改的欄位。
type 別名:幫型別取名字
type 可以幫任何型別取一個名字,不只是物件。
最小範例
type UserID = string | number;
let id1: UserID = "abc"; // OK,是文字
let id2: UserID = 123; // OK,是數字
let id3: UserID = true; // 錯誤!布林值不是 string 也不是 number
Code language: JavaScript (javascript)用 type 定義物件(和 interface 很像)
type User = {
name: string;
age: number;
email?: string;
};
const alice: User = { name: "Alice", age: 25 }; // 用法跟 interface 一模一樣
Code language: JavaScript (javascript)翻譯對照
| 你會看到 | 意思 |
|---|---|
type UserID = string |
「UserID 就是 string 的別名」 |
type UserID = string \| number |
「UserID 可以是 string 或 number」 |
type User = { name: string } |
「User 是一個有 name 欄位的物件」 |
interface vs type:差在哪?
這是很多人困惑的地方,但對 Vibe Coder 來說,記住兩件事就好:
差異一:擴展方式不同
// interface 用 extends 擴展
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// Dog 有 name 和 breed 兩個欄位
// type 用 & 組合
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
// Dog 同樣有 name 和 breed 兩個欄位
Code language: PHP (php)翻譯:兩種方式結果一樣,都是「Dog 繼承了 Animal 的欄位,再加上自己的」。只是寫法不同。
差異二:type 能做的事比較多
// type 可以定義聯合型別,interface 不行
type Status = "active" | "inactive" | "banned";
// type 可以定義元組,interface 不行
type Coordinate = [number, number];
// type 可以用基本型別,interface 不行
type UserID = string | number;
Code language: JavaScript (javascript)什麼時候用哪個?
| 場景 | 建議用 | 理由 |
|---|---|---|
| 定義物件結構 | interface |
最常見的慣例 |
| 聯合型別 | type |
interface 做不到 |
| 基本型別別名 | type |
interface 做不到 |
| 不確定 | 都行 | 結果幾乎一樣 |
結論:AI 產出的程式碼兩種都會用。看到 interface 或 type 定義物件時,讀法一模一樣,不用擔心。
常見變化
AI 產出的程式碼中,你常會遇到以下幾種寫法:
變化 1:巢狀物件型別
interface User {
name: string;
address: { // address 本身也是一個物件
city: string;
zipCode: string;
};
}
const alice: User = {
name: "Alice",
address: {
city: "Taipei",
zipCode: "100",
},
};
Code language: PHP (php)翻譯:「User 有一個 address 欄位,而 address 裡面又有 city 和 zipCode。」就是物件裡面包物件。
也可以拆開寫(比較常見的做法):
interface Address {
city: string;
zipCode: string;
}
interface User {
name: string;
address: Address; // 直接引用另一個 interface
}
Code language: PHP (php)翻譯:「先定義 Address 結構,再讓 User 的 address 欄位使用 Address 結構。」效果一模一樣,但更好讀。
變化 2:函式型別的定義
// 定義一個函式的型別
type GreetFn = (name: string) => string;
const greet: GreetFn = (name) => `Hello, ${name}`;
Code language: JavaScript (javascript)翻譯:「GreetFn 是一個函式型別:接收一個 string 參數,回傳一個 string。greet 這個函式必須符合這個型別。」
用 interface 也可以寫函式型別(但比較少見):
interface GreetFn {
(name: string): string;
}
Code language: PHP (php)翻譯:效果一樣,但用 type 寫更直覺,所以 AI 通常會用 type 來定義函式型別。
變化 3:多個 interface 組合
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
interface User extends HasName, HasAge {
email: string;
}
// User 有 name、age、email 三個欄位
Code language: PHP (php)翻譯:「User 同時繼承了 HasName 和 HasAge,再加上自己的 email。」一個 interface 可以繼承多個。
必看懂 vs 知道就好
必看懂(會一直出現)
interface User { name: string; }— 定義物件結構email?: string— 可選欄位readonly id: number— 唯讀欄位type UserID = string | number— 型別別名extends和&— 擴展或組合型別
知道就好(遇到再查)
interface的 declaration merging(同名 interface 會自動合併)- 泛型 interface
interface Box<T> { value: T }(第 4 篇會教) keyof、typeof、mapped types 等進階型別操作Partial<T>、Required<T>、Pick<T, K>等工具型別
Vibe Coder 檢查點
看到 AI 產出的 interface 或 type 時,確認這些事:
- [ ] 每個欄位的型別合理嗎?(例如
age應該是number,不是string) - [ ] 可選欄位
?的設定合理嗎?(真的可以不填的才應該是可選) - [ ] 必填欄位有沒有漏?(例如建立 User 通常需要 name)
- [ ] 巢狀物件有沒有拆出獨立的 interface?(拆開比較好讀)
- [ ]
readonly用在正確的地方嗎?(id、建立時間等不該被改的欄位)
本篇重點回顧
| 語法 | 意思 | 範例 |
|---|---|---|
interface |
定義物件結構 | interface User { name: string } |
type |
幫型別取名字 | type ID = string \| number |
? |
可選欄位 | email?: string |
readonly |
唯讀,不能改 | readonly id: number |
extends |
interface 的擴展 | interface Dog extends Animal |
& |
type 的組合 | type Dog = Animal & { breed: string } |
下一篇,我們會看 TypeScript 的泛型 <T> — AI 超愛用的型別參數化寫法。
進階測驗:interface 與 type — 定義物件的形狀
共 5 題,包含情境題與錯誤診斷題。
1. AI 幫你生成了一個 API 回傳的使用者資料型別,你需要決定哪些欄位該設為可選。以下哪個設計最合理? 情境題
情境:你的應用程式有一個使用者註冊頁面,name 和 email 是必填欄位,phone 和 bio 是選填欄位。