第 6 課:介面 (Interfaces)
學習目標
- 理解介面在 TypeScript 中的核心作用:定義契約
- 掌握如何使用介面定義物件的形狀,包括唯讀和可選屬性
- 學會定義函式類型介面、可索引類型介面和類別類型介面
- 理解並應用介面繼承
- 初步認識混合類型介面
- 能夠運用介面提升程式碼的可讀性和可維護性
1. 介面的基本概念
介面 (Interface) 是 TypeScript 的核心原則之一,用於「描述」物件的形狀或契約 (contract)。它們定義了物件應該包含哪些屬性以及這些屬性的類型。
為什麼需要介面?
定義契約:介面好像一份合約,規定了物件應該有什麼。用了介面,你就可以確保傳入函式的物件或者類別的實例符合特定的結構。
提高可讀性:透過介面,其他人(或者未來的你)可以很快地理解一個物件應該有什麼屬性和方法。
促進團隊協作:在團隊開發中,介面可以作為前後端或者不同模組之間的溝通橋樑,確保大家對數據結構有一致的理解。
更好的工具支援:IDE(例如 Visual Studio Code)可以根據介面提供更好的自動完成和錯誤提示。
🎯 互動練習 1:建立你的第一個介面
💡 參考答案
typescript
1// 正確的 User 介面定義
2interface User {
3 id: number;
4 name: string;
5 email: string;
6}
7
8// 符合介面的 user 物件
9const user: User = {
10 id: 1,
11 name: "小明",
12 email: "xiaoming@example.com"
13};
14
15function displayUser(user: User): void {
16 console.log(`用戶 ID: ${user.id}`);
17 console.log(`姓名: ${user.name}`);
18 console.log(`電郵: ${user.email}`);
19}
20
21displayUser(user);2. 可選屬性 (?)
有時我們希望介面中的某些屬性是可有可無的。這個時候就可以用 ? 符號將屬性標記為可選的。
typescript
1interface Product {
2 id: number;
3 name: string;
4 price: number;
5 description?: string; // 可選屬性
6 category?: string; // 可選屬性
7}🎯 互動練習 2:使用可選屬性
💡 參考答案
typescript
1interface Product {
2 id: number;
3 name: string;
4 price: number;
5 description?: string; // 可選屬性
6 category?: string; // 可選屬性
7}
8
9function createProduct(config: Product): void {
10 console.log(`產品: ${config.name} - $${config.price}`);
11
12 // 處理可選的 description 屬性
13 const desc = config.description ? config.description : "無描述";
14 console.log(`描述: ${desc}`);
15
16 // 處理可選的 category 屬性
17 const cat = config.category ? config.category : "未分類";
18 console.log(`類別: ${cat}`);
19}
20
21// 測試數據(請勿修改)
22const laptop: Product = {
23 id: 1,
24 name: "筆記本電腦",
25 price: 1200,
26 description: "高性能筆記本電腦"
27};
28
29const mouse: Product = {
30 id: 2,
31 name: "滑鼠",
32 price: 25,
33 category: "電腦配件"
34};
35
36createProduct(laptop);
37console.log("---");
38createProduct(mouse);3. 唯讀屬性 (readonly)
介面中的屬性可以用 readonly 修飾符標記,表示這些屬性在物件創建後就不可以再被修改。
typescript
1interface Point {
2 readonly x: number;
3 readonly y: number;
4}
5
6let point: Point = { x: 10, y: 20 };
7// point.x = 5; // 錯誤:Cannot assign to 'x' because it is a read-only property🎯 互動練習 3:唯讀屬性實作
4. 函式類型介面
介面不單止可以描述帶有屬性的物件,也可以描述函式類型。函式類型介面定義了函式的參數列表和回傳類型。
typescript
1interface SearchFunc {
2 (source: string, subString: string): boolean;
3}
4
5let mySearch: SearchFunc = function(src, sub) {
6 return src.search(sub) > -1;
7};🎯 互動練習 4:函式類型介面
💡 參考答案
typescript
1// 定義計算函式的介面
2interface Calculator {
3 (a: number, b: number): number;
4}
5
6// 實作加法函式
7const add: Calculator = function(a, b) {
8 return a + b;
9};
10
11// 實作減法函式
12const subtract: Calculator = function(a, b) {
13 return a - b;
14};
15
16// 實作乘法函式
17const multiply: Calculator = function(a, b) {
18 return a * b;
19};
20
21// 實作除法函式
22const divide: Calculator = function(a, b) {
23 return a / b;
24};
25
26// 測試函式(請勿修改)
27console.log("計算結果:", add(5, 3));
28console.log("計算結果:", subtract(5, 3));
29console.log("計算結果:", multiply(5, 3));
30console.log("計算結果:", divide(15, 5));5. 介面繼承
介面可以相互繼承,這讓我們能夠將介面分割成可重用的組件。
typescript
1interface Shape {
2 color: string;
3}
4
5interface Square extends Shape {
6 sideLength: number;
7}
8
9let square: Square = {
10 color: "blue",
11 sideLength: 10
12};🎯 互動練習 5:介面繼承實作
💡 參考答案
typescript
1// 基本動物介面
2interface Animal {
3 name: string;
4 age: number;
5 species: string;
6}
7
8// Pet 介面,繼承 Animal 並添加額外屬性
9interface Pet extends Animal {
10 owner: string;
11 isNeutered: boolean;
12}
13
14// 創建一個符合 Pet 介面的物件
15const myPet: Pet = {
16 name: "小白",
17 age: 3,
18 species: "貓",
19 owner: "小明",
20 isNeutered: true
21};
22
23function displayPetInfo(pet: Pet): void {
24 console.log("動物資訊:");
25 console.log(`名字: ${pet.name}`);
26 console.log(`年齡: ${pet.age}`);
27 console.log(`種類: ${pet.species}`);
28 console.log(`主人: ${pet.owner}`);
29 console.log(`是否已絕育: ${pet.isNeutered}`);
30}
31
32displayPetInfo(myPet);6. 可索引類型介面
TypeScript 支援兩種索引簽名:字串和數字。
typescript
1interface StringArray {
2 [index: number]: string;
3}
4
5interface StringDictionary {
6 [key: string]: string;
7}🎯 互動練習 6:可索引類型
💡 參考答案
typescript
1// 定義一個可以用字串索引的成績介面
2interface Grades {
3 [studentName: string]: number;
4}
5
6// 創建成績物件
7const studentGrades: Grades = {
8 "小明": 85,
9 "小華": 92,
10 "小李": 78
11};
12
13function displayGrades(grades: Grades): void {
14 console.log("學生成績:");
15
16 let total = 0;
17 let count = 0;
18
19 for (const student in grades) {
20 console.log(`${student}: ${grades[student]}`);
21 total += grades[student];
22 count++;
23 }
24
25 const average = Math.round(total / count);
26 console.log(`平均分: ${average}`);
27}
28
29displayGrades(studentGrades);7. 綜合練習
🎯 互動練習 7:圖書管理系統
💡 參考答案
typescript
1// 定義基本書籍介面
2interface Book {
3 title: string;
4 author: string;
5 pages: number;
6}
7
8// 定義可借閱書籍介面,繼承 Book
9interface BorrowableBook extends Book {
10 category: string;
11 isAvailable: boolean;
12 borrower?: string; // 可選屬性
13}
14
15// 創建兩本書
16const book1: BorrowableBook = {
17 title: "TypeScript 入門",
18 author: "張三",
19 pages: 300,
20 category: "教育",
21 isAvailable: true
22};
23
24const book2: BorrowableBook = {
25 title: "哈利波特",
26 author: "J.K. 羅琳",
27 pages: 500,
28 category: "小說",
29 isAvailable: false,
30 borrower: "小明"
31};
32
33function displayBook(book: BorrowableBook): void {
34 console.log(`圖書: ${book.title}`);
35 console.log(`作者: ${book.author}`);
36 console.log(`頁數: ${book.pages}`);
37 console.log(`類型: ${book.category}`);
38
39 if (book.isAvailable) {
40 console.log("是否可借: 是");
41 } else {
42 console.log("是否可借: 否");
43 if (book.borrower) {
44 console.log(`借閱者: ${book.borrower}`);
45 }
46 }
47}
48
49// 測試(請勿修改)
50console.log("=== 圖書館系統 ===");
51displayBook(book1);
52console.log("---");
53displayBook(book2);8. 知識檢查
🎯 互動練習 8:介面知識測驗
💡 參考答案
typescript
1// 介面知識測驗 - 正確答案
2let answer1: string = "B"; // 介面的主要作用是定義物件的結構和契約
3let answer2: string = "A"; // 可選屬性使用 ? 符號
4let answer3: string = "C"; // 唯讀屬性使用 readonly 關鍵字
5let answer4: string = "B"; // 介面可以繼承其他介面,使用 extends 關鍵字
6
7// 顯示答案(請勿修改下面的程式碼)
8console.log("第1題答案:" + answer1);
9console.log("第2題答案:" + answer2);
10console.log("第3題答案:" + answer3);
11console.log("第4題答案:" + answer4);總結
今日學到的重點
- 介面定義契約:使用
interface關鍵字定義物件的結構 - 可選屬性:使用
?讓屬性變成可選的 - 唯讀屬性:使用
readonly讓屬性不可修改 - 函式類型介面:定義函式的參數和返回值類型
- 介面繼承:使用
extends讓介面繼承其他介面 - 可索引類型:使用索引簽名定義動態屬性
課後建議
- 練習定義不同類型的介面
- 嘗試使用介面繼承來組織程式碼
- 在實際專案中使用介面來提高程式碼質量
- 重複做上面的互動練習,直到完全理解
下一課預告
下一課我們會學習:
- 類別的基本語法和建構函式
- 類別繼承和方法覆寫
- 存取修飾符(public、private、protected)
- 靜態屬性和方法
恭喜你完成第六課! 🎉
你已經掌握了 TypeScript 介面的核心概念。介面是 TypeScript 中非常重要的特性,它能幫助你寫出更安全、更清晰的程式碼!