强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

TypeScript 开发指南 / 06 - 类

TypeScript 的类在 JavaScript 类的基础上增加了类型注解、访问修饰符和抽象类等特性。

基本类定义

class User {
  // 属性声明
  name: string;
  age: number;

  // 构造函数
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // 方法
  greet(): string {
    return `你好,我是 ${this.name},今年 ${this.age} 岁。`;
  }
}

const alice = new User("Alice", 25);
console.log(alice.greet());

简写构造函数(Parameter Properties)

// 简写:参数前加修饰符,自动声明并赋值属性
class User {
  constructor(
    public name: string,
    public age: number,
    private password: string,
    readonly id: number
  ) {}
}

// 等价于
class User {
  public name: string;
  public age: number;
  private password: string;
  readonly id: number;

  constructor(name: string, age: number, password: string, id: number) {
    this.name = name;
    this.age = age;
    this.password = password;
    this.id = id;
  }
}

访问修饰符(Access Modifiers)

TypeScript 提供三种访问修饰符:

修饰符类内子类类外
public
protected
private
class Animal {
  public name: string;          // 公开
  protected species: string;    // 受保护
  private secret: string;       // 私有

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

  // 公开方法
  public getName(): string {
    return this.name;
  }

  // 受保护方法
  protected getSpecies(): string {
    return this.species;
  }

  // 私有方法
  private getSecret(): string {
    return this.secret;
  }
}

class Dog extends Animal {
  bark(): string {
    console.log(this.name);      // ✅ public
    console.log(this.species);   // ✅ protected
    // console.log(this.secret); // ❌ private 不可访问
    return "Woof!";
  }
}

const dog = new Dog("Buddy", "Canine", "secret");
console.log(dog.name);       // ✅ public
// console.log(dog.species); // ❌ protected
// console.log(dog.secret);  // ❌ private

ES2022 私有字段(#)

class Account {
  #balance: number = 0;  // 真正的私有字段(运行时也私有)

  deposit(amount: number): void {
    this.#balance += amount;
  }

  getBalance(): number {
    return this.#balance;
  }
}

const account = new Account();
account.deposit(100);
console.log(account.getBalance()); // 100
// console.log(account.#balance); // ❌ 语法错误
特性private#
编译时检查
运行时私有❌(编译后为普通属性)
与 JavaScript 兼容
反射访问可以不可以

存取器(Accessors)

class Temperature {
  private _celsius: number;

  constructor(celsius: number) {
    this._celsius = celsius;
  }

  // getter
  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32;
  }

  // setter
  set fahrenheit(value: number) {
    this._celsius = (value - 32) * 5 / 9;
  }

  get celsius(): number {
    return this._celsius;
  }

  set celsius(value: number) {
    if (value < -273.15) {
      throw new Error("温度不能低于绝对零度");
    }
    this._celsius = value;
  }
}

const temp = new Temperature(100);
console.log(temp.fahrenheit); // 212
temp.fahrenheit = 32;
console.log(temp.celsius);    // 0

继承(Inheritance)

// 基类
class Shape {
  constructor(public color: string) {}

  area(): number {
    return 0;
  }

  describe(): string {
    return `这是一个 ${this.color} 的图形,面积为 ${this.area()}`;
  }
}

// 派生类
class Circle extends Shape {
  constructor(color: string, public radius: number) {
    super(color); // 调用父类构造函数
  }

  // 重写方法
  override area(): number {
    return Math.PI * this.radius ** 2;
  }
}

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

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

const circle = new Circle("红色", 10);
console.log(circle.describe()); // 这是一个 红色 的图形,面积为 314.159...

const rect = new Rectangle("蓝色", 20, 30);
console.log(rect.describe());   // 这是一个 蓝色 的图形,面积为 600

注意:使用 override 关键字可以明确标识重写的方法,如果父类没有该方法会报错。

抽象类(Abstract Classes)

抽象类不能被实例化,只能被继承:

abstract class Shape {
  constructor(public color: string) {}

  // 抽象方法:子类必须实现
  abstract area(): number;
  abstract perimeter(): number;

  // 普通方法:子类可以直接使用
  describe(): string {
    return `${this.color}图形,面积=${this.area().toFixed(2)}`;
  }
}

// ❌ 不能实例化抽象类
// const shape = new Shape("红色"); // 错误

class Circle extends Shape {
  constructor(color: string, private radius: number) {
    super(color);
  }

  // 必须实现抽象方法
  area(): number {
    return Math.PI * this.radius ** 2;
  }

  perimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

class Triangle extends Shape {
  constructor(
    color: string,
    private a: number,
    private b: number,
    private c: number
  ) {
    super(color);
  }

  area(): number {
    const s = (this.a + this.b + this.c) / 2;
    return Math.sqrt(s * (s - this.a) * (s - this.b) * (s - this.c));
  }

  perimeter(): number {
    return this.a + this.b + this.c;
  }
}

const shapes: Shape[] = [
  new Circle("红", 5),
  new Triangle("蓝", 3, 4, 5)
];

shapes.forEach(shape => {
  console.log(shape.describe());
});

实现接口(Implements)

interface Serializable {
  serialize(): string;
  deserialize(data: string): void;
}

interface Printable {
  print(): void;
}

// 一个类可以实现多个接口
class Document implements Serializable, Printable {
  private content: string = "";

  serialize(): string {
    return JSON.stringify({ content: this.content });
  }

  deserialize(data: string): void {
    const parsed = JSON.parse(data);
    this.content = parsed.content;
  }

  print(): void {
    console.log(this.content);
  }
}

静态成员(Static Members)

class MathUtils {
  static readonly PI = 3.14159265358979;
  static readonly E = 2.71828182845905;

  static clamp(value: number, min: number, max: number): number {
    return Math.min(Math.max(value, min), max);
  }

  static lerp(start: number, end: number, t: number): number {
    return start + (end - start) * t;
  }
}

// 通过类名访问,不需要实例化
console.log(MathUtils.PI);
console.log(MathUtils.clamp(15, 0, 10)); // 10

Mixins(混入)

Mixin 是一种组合模式,允许将多个类的功能组合在一起:

// 基础构造函数类型
type Constructor<T = {}> = new (...args: any[]) => T;

// Mixin 工厂函数
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    createdAt = new Date();
    updatedAt = new Date();

    touch() {
      this.updatedAt = new Date();
    }
  };
}

function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActive = false;

    activate() {
      this.isActive = true;
    }

    deactivate() {
      this.isActive = false;
    }
  };
}

// 基础类
class User {
  constructor(public name: string) {}
}

// 组合多个 Mixin
const EnhancedUser = Timestamped(Activatable(User));

const user = new EnhancedUser("Alice");
user.activate();          // 来自 Activatable
user.touch();             // 来自 Timestamped
console.log(user.name);   // 来自 User
console.log(user.isActive);    // true
console.log(user.createdAt);   // Date

类与接口的配合

// 定义接口
interface Repository<T> {
  findById(id: number): T | undefined;
  findAll(): T[];
  save(entity: T): void;
  delete(id: number): boolean;
}

// 实现接口
interface User {
  id: number;
  name: string;
  email: string;
}

class InMemoryUserRepository implements Repository<User> {
  private users: Map<number, User> = new Map();
  private nextId = 1;

  findById(id: number): User | undefined {
    return this.users.get(id);
  }

  findAll(): User[] {
    return Array.from(this.users.values());
  }

  save(user: User): void {
    if (!user.id) {
      user.id = this.nextId++;
    }
    this.users.set(user.id, user);
  }

  delete(id: number): boolean {
    return this.users.delete(id);
  }
}

业务场景:状态机

type OrderStatus = "pending" | "paid" | "shipped" | "delivered" | "cancelled";

interface OrderState {
  readonly status: OrderStatus;
  pay(): OrderState;
  ship(): OrderState;
  deliver(): OrderState;
  cancel(): OrderState;
}

abstract class BaseOrderState implements OrderState {
  constructor(public readonly status: OrderStatus) {}

  pay(): OrderState {
    throw new Error(`无法从 ${this.status} 状态付款`);
  }

  ship(): OrderState {
    throw new Error(`无法从 ${this.status} 状态发货`);
  }

  deliver(): OrderState {
    throw new Error(`无法从 ${this.status} 状态确认收货`);
  }

  cancel(): OrderState {
    throw new Error(`无法从 ${this.status} 状态取消`);
  }
}

class PendingState extends BaseOrderState {
  constructor() { super("pending"); }
  pay() { return new PaidState(); }
  cancel() { return new CancelledState(); }
}

class PaidState extends BaseOrderState {
  constructor() { super("paid"); }
  ship() { return new ShippedState(); }
  cancel() { return new CancelledState(); }
}

class ShippedState extends BaseOrderState {
  constructor() { super("shipped"); }
  deliver() { return new DeliveredState(); }
}

class DeliveredState extends BaseOrderState {
  constructor() { super("delivered"); }
}

class CancelledState extends BaseOrderState {
  constructor() { super("cancelled"); }
}

// 使用
let order: OrderState = new PendingState();
order = order.pay();     // pending → paid
order = order.ship();    // paid → shipped
order = order.deliver(); // shipped → delivered

注意事项

  1. 优先使用 interface 而非 abstract class 来定义契约——接口更灵活
  2. 使用 override 关键字标识重写的方法
  3. 参数属性可以简化构造函数代码
  4. # 私有字段private 更安全(运行时也私有)
  5. Mixin 模式适合需要多重继承的场景

扩展阅读