抽象类与 implements

2026-09-09 00:00    #typescript  

interface 是纯契约,抽象类是"部分实现 + 部分契约"。implements 只检查形状,不继承任何实现。这三者的分工决定了代码结构。

三者对比

  1// 抽象类与 interface 的分工
  2
  3// ---- interface:纯类型契约,无实现 ----
  4interface Serializable {
  5  serialize(): string;
  6}
  7
  8// interface 可以有可选成员和只读成员
  9interface Config {
 10  readonly name: string;
 11  debug?: boolean;
 12}
 13
 14// ---- 抽象类:部分实现 + 强制子类实现部分 ----
 15abstract class Shape {
 16  // 抽象成员:子类必须实现
 17  abstract area(): number;
 18  abstract readonly kind: string;
 19
 20  // 已实现成员:子类继承
 21  describe(): string {
 22    return `${this.kind}: ${this.area().toFixed(2)}`;
 23  }
 24
 25  // 模板方法:定义流程,子类填细节
 26  report(): string {
 27    return `[${this.describe()}]`;
 28  }
 29
 30  // 抽象类可以有构造函数和状态
 31  protected label: string;
 32  constructor(label: string) {
 33    this.label = label;
 34  }
 35}
 36
 37class Circle extends Shape {
 38  readonly kind = "circle";
 39  constructor(private radius: number) { super("circle"); }
 40  area(): number { return Math.PI * this.radius ** 2; }
 41}
 42
 43// ---- 一个类可以 implements 多个 interface,但只能 extends 一个类 ----
 44interface Named { name: string }
 45interface Aged { age: number }
 46
 47class Person implements Named, Aged {
 48  constructor(public name: string, public age: number) {}
 49}
 50
 51// ---- implements 只检查形状,不继承实现 ----
 52class Version implements Serializable {
 53  constructor(private major: number) {}
 54  serialize(): string { return String(this.major); }
 55}
 56// 必须自己实现 serialize,implements 不给任何实现
 57
 58// ---- 抽象类也可以 implements interface ----
 59abstract class BaseEntity implements Serializable {
 60  abstract serialize(): string;      // 声明为抽象,交给子类
 61  id = 0;
 62}
 63
 64class UserEntity extends BaseEntity {
 65  serialize(): string { return JSON.stringify({ id: this.id }); }
 66}
 67
 68// ---- 抽象类的实例侧与静态侧 ----
 69abstract class Factory {
 70  abstract create(): string;
 71  static registry: string[] = [];
 72}
 73// 静态成员可以正常访问
 74Factory.registry.push("a");
 75// @ts-expect-error TS2511: Cannot create an instance of an abstract class.
 76new Factory();
 77
 78// ---- 选择标准 ----
 79// 用 interface:只要契约,多个类要共享形状,需要多实现
 80// 用抽象类:需要共享实现、需要 protected 成员、需要模板方法
 81
 82// ---- 混合:抽象类 + interface 组合 ----
 83interface Drawable { draw(): string }
 84abstract class Renderer {
 85  abstract render(target: Drawable): string;
 86  log(msg: string): string { return `[log] ${msg}`; }
 87}
 88class SvgRenderer extends Renderer {
 89  render(target: Drawable): string {
 90    return this.log(target.draw());
 91  }
 92}
 93
 94// ---- 抽象类的 protected 成员不能被外部访问 ----
 95class Sub extends Shape {
 96  readonly kind = "sub";
 97  area(): number { return 1; }
 98  readLabel(): string { return this.label; }    // ✓ protected 可被子类访问
 99}
100
101const shapes: Shape[] = [new Circle(1), new Sub("s")];
102console.log(shapes.map((s) => s.report()));
103console.log(new Person("a", 1), new Version(1).serialize(), new UserEntity().serialize());
104console.log(new SvgRenderer().render({ draw: () => "svg" }), new Sub("x").readLabel());
105console.log(Factory.registry);
1[ '[circle: 3.14]', '[sub: 1.00]' ]
2Person { name: 'a', age: 1 } 1 {"id":0}
3[log] svg x
4[ 'a' ]
interface抽象类普通类
有实现✗部分✓
有状态(字段)✗✓✓
能实例化✗✗✓
extends 数量多个(extends A, B)1 个1 个
implements 数量—多个多个
访问修饰符✗✓(protected 等)✓

抽象类的两个作用

作用一:不能实例化

1abstract class Factory {
2  abstract create(): string;
3}
4
5new Factory();
6// error TS2511: Cannot create an instance of an abstract class.

作用二:模板方法模式

父类定义流程,子类只填细节:

 1abstract class Shape {
 2  abstract area(): number;
 3  abstract readonly kind: string;
 4
 5  // 已实现:子类继承
 6  describe(): string {
 7    return `${this.kind}: ${this.area().toFixed(2)}`;
 8  }
 9
10  // 模板方法:定义流程,调用抽象成员
11  report(): string {
12    return `[${this.describe()}]`;
13  }
14}

report() 调用了 describe(),describe() 调用了抽象的 area()。子类只需实现 area() 和 kind,就自动获得了完整的 report() 能力。

implements 只检查形状

1interface Serializable {
2  serialize(): string;
3}
4
5class Version implements Serializable {
6  serialize(): string { return String(this.major); }   // 必须自己实现
7}

implements 不提供任何实现,它只是"编译期承诺这个类满足这个接口"。

数量限制

 1// ✓ 可以实现多个接口
 2class Person implements Named, Aged {
 3  constructor(public name: string, public age: number) {}
 4}
 5
 6// ✓ 抽象类也可以 implements
 7abstract class BaseEntity implements Serializable {
 8  abstract serialize(): string;    // 声明为抽象,交给子类
 9  id = 0;
10}
11
12// ✗ 只能继承一个类
13// class X extends A, B {}

interface 之间可以多继承:

1interface Combined extends Named, Aged {}

抽象类与 protected

抽象类独有的能力:protected 成员能被继承但对外不可见。

 1abstract class Shape {
 2  protected label: string;
 3  constructor(label: string) { this.label = label; }
 4}
 5
 6class Sub extends Shape {
 7  readLabel(): string { return this.label; }    // ✓ 子类可访问
 8}
 9
10new Sub("x").label;
11// error TS2445: Property 'label' is protected and only accessible within class
12// 'Shape' and its subclasses.

这是 interface 做不到的——接口只有公开成员。

选择标准

需求用什么
只要契约interface
多个不相关的类共享形状interface
需要共享实现抽象类
需要 protected 成员抽象类
需要模板方法模式抽象类
需要状态(字段)抽象类
实践建议

优先用 interface。它的约束更弱、组合更灵活(一个类可以实现多个接口),也避免了继承层级过深的问题。

只有在"确实要共享实现"或"需要 protected"时才用抽象类。这是"组合优于继承"原则在 TypeScript 里的体现。

混合使用

两者可以配合:

 1interface Drawable { draw(): string }
 2
 3abstract class Renderer {
 4  abstract render(target: Drawable): string;
 5  log(msg: string): string { return `[log] ${msg}`; }
 6}
 7
 8class SvgRenderer extends Renderer {
 9  render(target: Drawable): string {
10    return this.log(target.draw());     // 复用了父类的 log
11  }
12}

Drawable 是契约(很多不相关的类都可以实现),Renderer 提供共享实现(log)。

相关