TypeScript 介面 (Interface)
在「物件型別」那一章我們提過 interface 可以用來描述物件的形狀。但在物件導向程式設計 (OOP) 中,Interface 有更深層的意義:契約 (Contract)。
什麼是「契約」?
想像你是一個老闆,你要請一個「司機」。你不care他是男生女生、高矮胖瘦,你只care他有沒有駕照、會不會開車。
這裡的「司機規格書」就是 Interface。
interface Driver {
licenseId: string;
drive(): void;
}
這個 Interface 規定了:任何想當 Driver 的人,都必須擁有 licenseId 並且 會 drive()。
類別實作 (Class Implements)
類別可以使用 implements 關鍵字來簽署這份契約。
// ✅ 計程車司機:符合契約
class TaxiDriver implements Driver {
licenseId = 'A123456789';
drive() {
console.log('計程車出發!');
}
// 他可以有額外的技能,這不違反契約
collectFare() {
console.log('收錢');
}
}
// ❌ 賽車手:違約!(沒有 licenseId)
class Racer implements Driver {
// Error: Property 'licenseId' is missing in type 'Racer'
drive() {
console.log('飆車!');
}
}
這就是 Interface 強大的地方:它保證了行為的一致性。你的系統某個部分需要一個 Driver,你可以放心地把 TaxiDriver 丟進去,因為 TS 保證他一定會 drive()。
介面繼承 (Interface Extends)
Interface 也可以繼承其他的 Interface。這就像是契約的「增補條款」。
interface Animal {
eat(): void;
}
// Dog 繼承了 Animal 的所有規定,並且多了 bark
interface Dog extends Animal {
bark(): void;
}
const myDog: Dog = {
eat() {
console.log('Nom nom');
},
bark() {
console.log('Woof!');
},
};
implements vs extends
這是最容易搞混的:
extends(繼承):是「這是那種東西」。(例如:狗是動物)。子類別會繼承父類別的程式碼。implements(實作):是「這能做那件事」。(例如:手機能拍照)。類別不會繼承 Interface 的程式碼,只是被檢查「有沒有做這些事」。
一個類別只能 extends 一個父類別,但可以 implements 多個 Interface。
class SmartPhone extends Electronics implements Camera, MusicPlayer, Phone {
// ... 必須實作拍照、播音樂、打電話的所有功能
}
宣告合併 (Declaration Merging)
這是 Interface 獨有的黑魔法。如果你定義了兩次同名的 Interface,TS 會自動把它們「合體」。
interface Box {
height: number;
}
interface Box {
width: number;
}
// 現在 Box 同時有 height 和 width
const b: Box = { height: 10, width: 20 };
這在擴充第三方套件的型別時非常有用(例如幫 Window 物件加屬性)。type 則做不到這一點,會報錯「重複宣告」。