TypeScript 類別 (Class)

雖然現代 React/Vue 開發比較少用 Class,但在寫 Node.js 後端(如 NestJS)或封裝複雜邏輯時,Class 還是非常好用的。

TypeScript 的 Class 語法基本上跟 ES6 Class 一樣,但多了三個強大的功能:存取修飾子屬性初始化簡寫抽象類別

存取修飾子 (Access Modifiers)

這大概是 TS Class 最實用的功能了。我們可以控制誰能「看到」或「修改」類別裡的東西。

你可以把類別想像成一間「公司大樓」:

1. public (公開) - 大廳

任何人都可以直接進去,完全沒管制。這是預設值,如果你不寫修飾子,就是 public

class Employee {
  public name: string; // 其實可以省略 public

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

const e = new Employee('Alice');
console.log(e.name); // OK,外部可以存取

2. private (私有) - 你的私人置物櫃

只有你自己(類別內部)可以打開,其他人(外部程式、甚至是繼承你的子類別)都不能碰。

class BankAccount {
  private balance: number;

  constructor(initial: number) {
    this.balance = initial;
  }

  // 只能透過公開的方法來存取
  getBalance() {
    return this.balance;
  }
}

const account = new BankAccount(1000);
// account.balance; // 錯誤!balance 是私有的

注意:TypeScript 的 private 只是「編譯時期」的檢查。編譯成 JavaScript 後,這些屬性其實還是可以被存取的(雖然會有紅線警告)。如果你需要執行時期真正的私有屬性,請使用 JavaScript 原生的 # 語法(如 #balance)。

3. protected (受保護) - 員工辦公區

外部人員不能進來,但是「員工」(繼承的子類別)可以進來使用。

class Person {
  protected id: number = 123;
}

class Employee extends Person {
  showId() {
    console.log(this.id); // OK,子類別可以存取
  }
}

const p = new Person();
// p.id; // 錯誤!外部不能存取

屬性初始化簡寫 (Shorthand Initialization)

這是這寫 TS Class 最爽的功能。

傳統寫法非常囉唆:

  1. 先宣告屬性型別
  2. 在建構子宣告參數
  3. 在建構子裡 this.x = x
// ❌ 傳統寫法 (太長了!)
class Point {
  public x: number;
  public y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

✅ 簡寫法:直接在建構子參數前加修飾子 (public, private, readonly),TS 就會自動幫你宣告屬性並賦值。

class Point {
  // 自動宣告 x, y 屬性,並自動賦值
  constructor(
    public x: number,
    public y: number
  ) {}
}

const p = new Point(10, 20);
console.log(p.x); // 10

這在 Dependency Injection (DI) 模式(如 NestJS, Angular)中非常常用。

唯讀屬性 (readonly)

如果你希望某個屬性在初始化後就不能再被修改,可以加上 readonly

class User {
  constructor(
    public readonly id: string,
    public name: string
  ) {}
}

const u = new User('A001', 'Alice');
u.name = 'Bob'; // OK
// u.id = "A002"; // 錯誤!id 是唯讀的

靜態成員 (static)

static 屬性或方法是屬於「類別本身」的,而不是屬於「實例 (Instance)」的。通常用來放一些工具函式。

class MathUtil {
  static readonly PI = 3.14;

  static calculateArea(radius: number) {
    return this.PI * radius * radius;
  }
}

console.log(MathUtil.PI); // 不需要 new MathUtil() 就可以用