TypeScript中class的两种继承方式extends和implements的对比
在 TypeScript 中,extends
和 implements
是实现类继承的两种核心方式,它们的用途和语法差异如下:
一、核心区别
特性 | extends(类继承) | implements(接口实现) |
---|---|---|
继承对象 | 继承另一个类的属性和方法(包括实现)。 | 实现一个或多个接口,仅约束方法签名(不包含实现)。 |
语法 | class Child extends Parent { ... } | class MyClass implements Interface1, Interface2 { ... } |
继承数量 | 只能单继承(extends 后面只能跟一个类)。 | 可以多实现(implements 后面跟多个接口,用逗号分隔)。 |
构造函数 | 子类构造函数必须调用 super() 以初始化父类。 | 无特殊要求,只需实现接口中定义的成员。 |
用途 | 复用父类的代码和行为,扩展已有类。 | 确保类符合特定契约(接口),实现多态。 |
二、extends(类继承)的使用
1. 基本语法
class Animal {constructor(public name: string) {}speak() {console.log(`${this.name} makes a sound.`);}
}class Dog extends Animal {constructor(name: string, public breed: string) {super(name); // 必须调用父类构造函数}speak() {console.log(`${this.name} barks!`); // 方法重写}
}const dog = new Dog("Buddy", "Labrador");
dog.speak(); // 输出: "Buddy barks!"
2. 关键点
- 单继承:每个类只能有一个直接父类。
- 访问修饰符:子类可访问父类的
public
和protected
成员,但不能访问private
成员。 - 方法重写:子类可通过同名方法覆盖父类的实现。
三、implements(接口实现)的使用
1. 基本语法
interface Shape {area(): number;
}interface Printable {printInfo(): void;
}class Circle implements Shape, Printable {constructor(public radius: number) {}area() {return Math.PI * this.radius ** 2; // 实现 Shape 接口的方法}printInfo() {console.log(`Circle with radius ${this.radius}`); // 实现 Printable 接口的方法}
}
2. 关键点
- 多实现:一个类可以实现多个接口,用逗号分隔。
- 严格匹配:类必须实现接口中定义的所有成员(方法和属性),且类型必须兼容。
- 接口扩展:接口可继承其他接口,类需实现所有层级的成员。
四、对比示例
1. extends 的场景
class Vehicle {start() {console.log("Starting engine...");}
}class Car extends Vehicle {// 继承了 Vehicle 的 start() 方法drive() {this.start(); // 复用父类方法console.log("Driving...");}
}
2. implements 的场景
interface Logger {log(message: string): void;
}class ConsoleLogger implements Logger {log(message: string) {console.log(`[LOG] ${message}`); // 实现接口方法}
}class FileLogger implements Logger {log(message: string) {// 将消息写入文件的实现}
}
五、常见误区与注意事项
1. 不要混淆继承和实现
- extends:用于复用代码和行为,子类是父类的 “特殊化”(如
Dog
是Animal
的一种)。 - implements:用于约束类的结构,实现类和接口是 “契约” 关系(如
Circle
承诺实现Shape
接口)。
2. 接口 vs 抽象类
- 接口(Interface):只能定义方法签名,不能包含实现,支持多实现。
- 抽象类(Abstract Class):可包含抽象方法(必须由子类实现)和具体实现,只能单继承。
abstract class Animal {abstract speak(): void; // 抽象方法,必须由子类实现move() {console.log("Moving..."); // 具体方法,子类可直接继承} }
3. 接口可以继承类
接口可继承类的结构(包括私有和受保护成员),但只有该类的子类才能实现这个接口:
class Base {private x = 0;
}interface Derived extends Base {y: number;
}class Child extends Base implements Derived {y = 1; // 合法,Child 是 Base 的子类
}
六、何时选择 extends 或 implements
场景 | 推荐方式 | 原因 |
---|---|---|
复用已有类的代码和行为 | extends | 子类自动获得父类的所有属性和方法,只需扩展需要的部分。 |
定义类的契约(如插件系统) | implements | 多个类可实现同一接口,确保方法签名统一,便于替换和扩展。 |
需要部分实现 + 部分抽象 | 抽象类(abstract class ) | 结合了 extends 和抽象方法,强制子类实现特定部分,同时复用已有代码。 |
需要多继承特性 | implements | 类无法多继承,但可实现多个接口,间接实现 “多继承” 的效果。 |
总结
- extends:纵向扩展,让子类继承父类的属性和方法,强调 “是一种”(is-a)关系。
- implements:横向约束,让类遵循接口的契约,强调 “能做什么”(can-do)关系。
合理结合 extends
和 implements
,可构建出既复用代码又灵活可扩展的类型系统。