本文 AI 產出,尚未審核

TypeScript 類別(Classes)— 繼承(extends)

簡介

在物件導向程式設計裡,繼承是最核心的概念之一。它讓我們可以把共用的屬性與行為抽取到父類別(base class),子類別(derived class)只需要關注自己的特殊邏輯,從而達到 程式碼重用降低耦合度提升可維護性 的效果。
TypeScript 在 ES6 基礎上加入了靜態型別,讓繼承不只是語法層面的「copy‑paste」,而是能在編譯階段即捕捉到型別錯誤,提升開發者的信心。
本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整介紹在 TypeScript 中如何使用 extends 進行類別繼承,適合剛入門的朋友以及想深化了解的中級開發者。

核心概念

1. 基本語法與 super 關鍵字

在 TypeScript 中,使用 extends 讓一個類別繼承另一個類別。子類別會自動取得父類別的 publicprotected 成員,若要呼叫父類別的建構函式或方法,必須使用 super

class Animal {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }

  public move(distance: number): void {
    console.log(`${this.name} 移動了 ${distance} 公尺`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    // 必須先呼叫 super,才能使用 this
    super(name);
  }

  public bark(): void {
    console.log('汪汪!');
  }
}

const d = new Dog('小白');
d.bark();          // 汪汪!
d.move(10);        // 小白 移動了 10 公尺

重點:在子類別的建構函式裡,第一行 必須呼叫 super(...),否則編譯會錯誤。

2. 覆寫(override)與 protected 成員

子類別可以 覆寫 父類別的方法,只要保留相同的簽名即可。若父類別的方法被標記為 protected,則只能在子類別或同一個模組內部呼叫。

class Vehicle {
  protected speed: number = 0;

  public accelerate(delta: number): void {
    this.speed += delta;
    console.log(`速度提升至 ${this.speed} km/h`);
  }
}

class Car extends Vehicle {
  // 覆寫父類別的 accelerate,加入額外的檢查
  public accelerate(delta: number): void {
    if (delta < 0) {
      console.warn('加速值不可為負');
      return;
    }
    super.accelerate(delta); // 呼叫父類別的實作
  }
}

const myCar = new Car();
myCar.accelerate(20); // 速度提升至 20 km/h

3. 抽象類別(abstract class)

抽象類別無法直接實例化,只能作為其他類別的基底。它可以定義 抽象方法(沒有實作),強制子類別必須提供實作。

abstract class Shape {
  // 抽象方法,子類別必須實作
  abstract area(): number;

  public describe(): void {
    console.log(`此圖形的面積為 ${this.area()}`);
  }
}

class Rectangle extends Shape {
  constructor(private width: number, private height: number) {
    super();
  }

  public area(): number {
    return this.width * this.height;
  }
}

const rect = new Rectangle(5, 3);
rect.describe(); // 此圖形的面積為 15

4. 多層繼承與 instanceof 判斷

類別可以形成多層繼承鏈,instanceof 可用來檢測物件在繼承階層中的位置。

class Person {
  constructor(public name: string) {}
}

class Employee extends Person {
  constructor(name: string, public id: number) {
    super(name);
  }
}

class Manager extends Employee {
  constructor(name: string, id: number, public department: string) {
    super(name, id);
  }
}

const alice = new Manager('Alice', 101, '研發');
console.log(alice instanceof Manager);   // true
console.log(alice instanceof Employee); // true
console.log(alice instanceof Person);   // true

5. 使用 static 成員與繼承

static 成員屬於類別本身,子類別會繼承父類別的 static 成員,但不會自動繼承父類別的 實例屬性

class Counter {
  static total = 0;

  constructor() {
    Counter.total++;
  }

  static reset(): void {
    Counter.total = 0;
  }
}

class ClickCounter extends Counter {
  // 直接使用父類別的 static 成員
}

new Counter();
new ClickCounter();
new ClickCounter();

console.log(Counter.total); // 3
ClickCounter.reset();       // 重新歸零
console.log(Counter.total); // 0

常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記呼叫 super() 子類別建構函式若未第一行呼叫 super,會編譯錯誤或執行時錯誤。 確保 super(...args) 在建構函式的最前面。
覆寫時忘記加 override 修飾詞(TS 4.3+) 若父類別方法被覆寫,未加 override 會失去編譯期檢查。 使用 override 關鍵字,讓編譯器保證簽名相符。
過度使用繼承 繼承層級過深會導致維護困難,應優先考慮 組合(composition)或 介面(interface)。 只在 “is‑a” 關係明確時才使用繼承。
protected 成員被濫用 protected 讓子類別直接存取,若濫用會破壞封裝。 儘量保持屬性 private,提供必要的 protected 方法。
靜態成員的繼承混淆 靜態成員屬於類別本身,子類別繼承後仍屬於父類別,呼叫時要注意作用域。 使用 ClassName.staticMember 明確指向,必要時在子類別重新宣告。

最佳實踐

  1. 使用 abstract 強制契約:對於需多個子類別實作的行為,使用抽象類別或介面定義清晰的 API。
  2. 加上 override:在 TypeScript 4.3 以上,override 能防止因簽名變更而產生的隱性錯誤。
  3. 限制繼承深度:一般建議不超過 3 層,超過時請檢視是否可以改以 組合Mixin
  4. 保持建構函式簡潔:讓子類別只傳遞必要參數給 super,避免在子類別中重複父類別的邏輯。
  5. 利用 readonly:對於不應被子類別修改的屬性,使用 readonly 加強型別安全。

實際應用場景

場景 需求 繼承解法
UI 元件庫 多種按鈕(PrimaryButton、DangerButton)共享樣式與事件處理 建立 BaseButton 抽象類別,子類別只覆寫顏色或文字。
遊戲角色系統 不同角色(Warrior、Mage)擁有共同的生命值、移動方式,但攻擊方式不同 Character 為基底,WarriorMage 繼承並實作各自的 attack()
資料存取層(DAO) 多種資料來源(MySQL、MongoDB)提供相同的 CRUD 介面 AbstractRepository<T> 抽象類別定義 create, read 等方法,具體實作在 MySQLRepositoryMongoRepository
日誌系統 基本日誌功能 + 不同環境的額外行為(檔案、遠端) Logger 基底提供 log()FileLoggerRemoteLogger 繼承並擴充。

總結

繼承 是 TypeScript 類別最具威力的特性之一,它不僅讓程式碼 重用結構化,更配合靜態型別提供 編譯期安全。本文從基本語法、super、覆寫、抽象類別,到多層繼承與 static 成員,都提供了實作範例與注意事項。
在實務開發中,適度使用繼承、配合介面與組合,才能寫出既易讀又易維護的程式碼。掌握了 extends 的正確用法後,你就能在大型專案中建立清晰的類別層級,讓團隊協作更順暢,未來也能更輕鬆地擴充功能。祝你在 TypeScript 的世界裡,玩得開心、寫得乾淨!