TypeScript 继承与多态
本文献给:
已掌握 TypeScript 类基础(字段、构造函数、方法、访问修饰符、参数属性)的开发者。本文将带你学习类的继承(extends 和 super)、方法重写与 override 关键字,以及多态在 TypeScript 中的实现方式。
你将学到:
extends关键字实现类继承super调用父类构造函数与方法- 方法重写与
override关键字(TS 4.3+) - 多态的概念与示例
- 继承与访问修饰符的交互规则
目录
一、extends 继承
使用 extends 关键字让一个类继承另一个类的所有属性和方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog("Buddy");
dog.bark(); // "Buddy barks"
dog.move(10); // "Buddy moved 10m"
子类拥有父类的所有 public 和 protected 成员,private 成员不可继承。
二、super 关键字
2.1 调用父类构造函数
如果在子类中定义了构造函数,则必须使用 super() 调用父类构造函数,且必须在访问 this 之前调用。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name); // 必须先调用 super
this.breed = breed;
}
}
2.2 调用父类方法
在子类方法中可以使用 super.methodName() 调用父类的原始方法。
class Animal {
speak() {
console.log("Animal makes sound");
}
}
class Dog extends Animal {
speak() {
super.speak(); // 调用父类方法
console.log("Dog barks");
}
}
const d = new Dog();
d.speak();
// "Animal makes sound"
// "Dog barks"
三、方法重写
子类可以重写父类的方法,提供自己的实现。
class Vehicle {
start(): void {
console.log("Vehicle starting...");
}
}
class Car extends Vehicle {
start(): void {
console.log("Car engine roaring...");
}
}
const myCar = new Car();
myCar.start(); // "Car engine roaring..."
3.1 override 关键字(TypeScript 4.3+)
TypeScript 4.3 引入了 override 关键字,用于显式标记方法重写父类方法。这可以避免因父类方法更名或删除而导致的意外错误。
class Parent {
greet() {
console.log("Hello");
}
}
class Child extends Parent {
override greet() {
console.log("Hi");
}
}
如果父类中没有 greet 方法,override 会报错:
class Child extends Parent {
override sayHi() { // ❌ 父类中没有 sayHi 方法
console.log("Hi");
}
}
override 关键字让代码意图更清晰,推荐在重写方法时使用。
四、多态
多态是指同一个方法在不同子类中表现出不同的行为。父类类型的变量可以指向子类对象,调用重写的方法时,实际执行的是子类的实现。
class Shape {
area(): number {
return 0;
}
}
class Circle extends Shape {
radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
override area(): number {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
width: number;
height: number;
constructor(width: number, height: number) {
super();
this.width = width;
this.height = height;
}
override area(): number {
return this.width * this.height;
}
}
function printArea(shape: Shape) {
console.log(`Area: ${shape.area()}`);
}
const shapes: Shape[] = [new Circle(5), new Rectangle(4, 6)];
shapes.forEach(printArea);
// Area: 78.53981633974483
// Area: 24
4.1 多态的优势
- 代码复用:使用父类类型的参数可以接受任意子类实例。
- 扩展性:新增子类无需修改已有函数。
五、继承与访问修饰符的交互
5.1 public
public 成员在子类中保持 public,可被任意访问。
5.2 protected
protected 成员在子类内部可访问,但通过子类实例在外部不可访问。
class Parent {
protected value = 42;
}
class Child extends Parent {
show() {
console.log(this.value); // OK
}
}
const c = new Child();
console.log(c.value); // ❌ 属性受保护
5.3 private
private 成员不被继承,子类中无法访问。
class Parent {
private secret = "hidden";
}
class Child extends Parent {
try() {
// console.log(this.secret); // ❌ 属性私有
}
}
5.4 子类可以放宽访问权限吗?
子类重写方法时,不能降低父类方法的访问权限(即不能将 public 改为 protected 或 private),但可以提升(将 protected 改为 public 是允许的)。
class Parent {
protected doSomething() {}
}
class Child extends Parent {
public doSomething() {} // OK,放宽权限
}
六、常见错误与注意事项
6.1 忘记调用 super
在子类构造函数中,如果省略 super(),TypeScript 会报错。
class Child extends Parent {
constructor() {
// 缺少 super() ❌
}
}
6.2 在 super 之前访问 this
class Child extends Parent {
constructor() {
console.log(this); // ❌ 在 super 之前不能使用 this
super();
}
}
6.3 重写方法时返回类型不兼容
重写方法的返回类型必须是父类方法返回类型的子类型。
class Parent {
get(): object {
return {};
}
}
class Child extends Parent {
override get(): string { // ❌ string 不是 object 的子类型
return "hello";
}
}
6.4 子类属性与父类属性同名
如果子类声明了与父类同名的属性,会覆盖父类的属性(但建议避免这样做,容易造成混淆)。
class Parent {
name = "Parent";
}
class Child extends Parent {
name = "Child"; // 覆盖
}
const c = new Child();
console.log(c.name); // "Child"
6.5 override 关键字版本要求
override 需要 TypeScript 4.3+ 且 noImplicitOverride 标志启用(或严格模式)。如果没有启用,override 只是普通标识符,但建议在 tsconfig.json 中开启 "noImplicitOverride": true。
七、综合示例
// 抽象一个媒体播放器基类
class MediaPlayer {
protected currentVolume: number = 50;
constructor(protected name: string) {}
play(): void {
console.log(`${this.name} starts playing`);
}
pause(): void {
console.log(`${this.name} pauses`);
}
setVolume(volume: number): void {
this.currentVolume = volume;
console.log(`Volume set to ${volume}`);
}
}
// 音频播放器子类
class AudioPlayer extends MediaPlayer {
private equalizer: boolean = false;
constructor(name: string, private bitrate: number) {
super(name);
}
override play(): void {
console.log(`AudioPlayer ${this.name} plays at ${this.bitrate}kbps`);
}
enableEqualizer(): void {
this.equalizer = true;
console.log("Equalizer enabled");
}
// 重写并调用父类方法
override setVolume(volume: number): void {
super.setVolume(volume);
console.log(`Audio volume adjusted to ${volume}`);
}
}
// 视频播放器子类
class VideoPlayer extends MediaPlayer {
private subtitlesEnabled: boolean = false;
override play(): void {
console.log(`VideoPlayer ${this.name} plays with subtitles: ${this.subtitlesEnabled}`);
}
toggleSubtitles(): void {
this.subtitlesEnabled = !this.subtitlesEnabled;
console.log(`Subtitles ${this.subtitlesEnabled ? "enabled" : "disabled"}`);
}
// 不重写 setVolume,沿用父类行为
}
// 多态使用
function controlMedia(players: MediaPlayer[]) {
players.forEach(player => {
player.play();
player.setVolume(70);
player.pause();
});
}
const audio = new AudioPlayer("Spotify", 320);
const video = new VideoPlayer("Netflix");
audio.enableEqualizer();
video.toggleSubtitles();
controlMedia([audio, video]);
// 检查类型
if (audio instanceof AudioPlayer) {
audio.enableEqualizer(); // 类型守卫后可用特有方法
}
八、小结
| 概念 | 语法/示例 | 说明 |
|---|---|---|
| 继承 | class Child extends Parent |
子类获得父类非私有成员 |
| super 调用构造函数 | super(args) |
子类构造函数中必须调用 |
| super 调用方法 | super.method() |
调用父类原始方法 |
| 方法重写 | 子类中定义同名方法 | 覆盖父类实现 |
| override 关键字 | override method() {} |
显式标记重写,增强安全性 |
| 多态 | 父类类型变量指向子类实例 | 调用重写方法执行子类版本 |
| 权限交互 | protected 可被继承,private 不可 |
子类可放宽但不能缩小访问权限 |
觉得文章有帮助?别忘了:
👍 点赞 👍 – 给我一点鼓励
⭐ 收藏 ⭐ – 方便以后查看
🔔 关注 🔔 – 获取更新通知
标签: #TypeScript #继承 #多态 #面向对象 #学习笔记 #前端开发
更多推荐
所有评论(0)