Skip to content
第 8 章 前端 ⏱ 12 分钟阅读

第 8 章:类与继承 ​

学习目标 ​

  • 用 TS 写类(属性、方法、构造器)
  • 学会访问修饰符 public / private / protected
  • 掌握继承、抽象类、实现接口
  • 理解参数属性、readonly

一、最基本的类 ​

typescript
class User {
  name: string;
  age: number;

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

  greet(): string {
    return `Hi, I'm ${this.name}`;
  }
}

const u = new User('Tom', 18);
u.greet();  // "Hi, I'm Tom"

二、访问修饰符 ​

typescript
class Account {
  public id: string;            // 默认 public,外部可访问
  private password: string;     // 仅本类内部访问
  protected email: string;      // 本类和子类可访问
  readonly createdAt: Date;     // 只读,只能赋值一次

  constructor(id: string, password: string, email: string) {
    this.id = id;
    this.password = password;
    this.email = email;
    this.createdAt = new Date();
  }
}

const acc = new Account('1', 'pwd', 'a@b.com');
acc.id;               // ✅
acc.password;         // ❌ Property 'password' is private
acc.createdAt;        // ✅ 可读
acc.createdAt = new Date();  // ❌ Cannot assign

⚠️ 坑 1:private 是编译期保护,运行后通过 (acc as any).password 仍能访问。需要真正私有得用 # 字段(ES2022)。

三、参数属性(简写) ​

typescript
class User {
  constructor(
    public name: string,
    private age: number,
    readonly id: number,
  ) {}

  greet(): string {
    return `${this.name}, ${this.age}岁`;
  }
}

等价于在构造函数里 this.name = name 这种样板代码。

四、继承(extends) ​

typescript
class Animal {
  constructor(public name: string) {}

  move(distance: number): void {
    console.log(`${this.name} 移动了 ${distance}m`);
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name);  // 必须先调 super
  }

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

  // 重写父类方法
  move(distance: number): void {
    console.log('跑...');
    super.move(distance);  // 调父类版本
  }
}

const d = new Dog('旺财', '柴犬');
d.move(10);  // 跑...\n 旺财 移动了 10m
d.bark();    // 汪汪!

⚠️ 坑 2:子类构造函数必须先调 super(),否则 this 不存在,会报 "super has to be called before accessing this"。

五、抽象类(abstract) ​

typescript
abstract class Shape {
  abstract area(): number;          // 抽象方法:不实现,子类必须重写
  describe(): void {                 // 具体方法:可以共用
    console.log(`面积:${this.area()}`);
  }
}

class Circle extends Shape {
  constructor(public r: number) {
    super();
  }
  area(): number {
    return Math.PI * this.r ** 2;
  }
}

const c = new Circle(5);
c.describe();  // 面积:78.53981633974483

// ❌ 抽象类不能 new
new Shape();  // Cannot create an instance of an abstract class

六、实现接口(implements) ​

typescript
interface Printable {
  print(): void;
}

interface Loggable {
  log(msg: string): void;
}

class Document implements Printable, Loggable {
  constructor(public content: string) {}

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

  log(msg: string): void {
    console.log(`[LOG] ${msg}`);
  }
}

⚠️ 坑 3:implements 只检查结构,不改变类的类型。即使实现了接口,类实例也不"等于"接口(强类型语言不一样)。

七、存取器(getter / setter) ​

typescript
class User {
  private _age: number = 0;

  get age(): number {
    return this._age;
  }

  set age(value: number) {
    if (value < 0) throw new Error('年龄不能为负');
    this._age = value;
  }
}

const u = new User();
u.age = 18;
console.log(u.age);  // 18
u.age = -1;          // ❌ Error

⚠️ 坑 4:getter / setter 必须配对出现(只读除外),且不能与同名属性冲突。

八、静态成员 ​

typescript
class Counter {
  static count: number = 0;

  static increment(): void {
    Counter.count++;
  }

  constructor() {
    Counter.increment();
  }
}

new Counter();
new Counter();
console.log(Counter.count);  // 2

九、本章小结 ​

要点关键
修饰符public(默认)/ private(类内)/ protected(子类)
只读readonly,只能赋值一次
参数属性构造器参数前加修饰符,自动赋值
继承extends + super(),子类必须先 super
抽象类abstract class,不能实例化,需子类实现抽象方法
接口implements 检查结构
静态static 属于类,不属于实例

动手练习 ​

  1. 封装:写 BankAccount,balance 用 private,提供 deposit / withdraw
  2. 继承:写 Employee extends Person,加 salary 字段和 work() 方法
  3. 抽象类:写 Shape 抽象类 + Triangle / Rectangle 子类,实现 area()

下一章:第 9 章:枚举 →

本站基于 VitePress 构建 · 由 StackHub 团队维护