本文献给:

已掌握 TypeScript 类基础(字段、构造函数、方法、访问修饰符、参数属性)的开发者。本文将带你学习类的继承(extendssuper)、方法重写与 override 关键字,以及多态在 TypeScript 中的实现方式。


你将学到:

  1. extends 关键字实现类继承
  2. super 调用父类构造函数与方法
  3. 方法重写与 override 关键字(TS 4.3+)
  4. 多态的概念与示例
  5. 继承与访问修饰符的交互规则



一、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"

子类拥有父类的所有 publicprotected 成员,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 改为 protectedprivate),但可以提升(将 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 #继承 #多态 #面向对象 #学习笔记 #前端开发

更多推荐