Skip to content

类继承

概述

类继承(Class Inheritance)是面向对象编程的核心特性之一,允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法。通过继承,我们可以实现代码复用、建立类之间的层次关系,并支持多态性。TypeScript 使用 extends 关键字实现类继承,子类可以访问父类的公共和受保护成员,并可以重写父类的方法。

基本语法

extends 关键字

使用 extends 关键字声明类继承关系:

typescript
// 父类(基类)
class Animal {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
  
  makeSound(): void {
    console.log(`${this.name} makes a sound`);
  }
}

// 子类(派生类)继承父类
class Dog extends Animal {
  breed: string;
  
  constructor(name: string, breed: string) {
    super(name);  // 调用父类构造函数
    this.breed = breed;
  }
  
  // 子类可以添加新方法
  bark(): void {
    console.log(`${this.name} barks: Woof!`);
  }
}

// 使用示例
const dog = new Dog("Buddy", "Golden Retriever");
dog.makeSound();  // "Buddy makes a sound"(继承自父类)
dog.bark();       // "Buddy barks: Woof!"(子类自己的方法)

super 关键字

super 关键字用于访问父类的成员,包括构造函数、方法和属性:

typescript
class Vehicle {
  protected speed: number;
  protected brand: string;
  
  constructor(brand: string, speed: number = 0) {
    this.brand = brand;
    this.speed = speed;
  }
  
  accelerate(amount: number): void {
    this.speed += amount;
    console.log(`${this.brand} accelerated to ${this.speed} km/h`);
  }
  
  getInfo(): string {
    return `${this.brand} - Speed: ${this.speed} km/h`;
  }
}

class Car extends Vehicle {
  private doors: number;
  
  constructor(brand: string, doors: number) {
    super(brand);  // 调用父类构造函数
    this.doors = doors;
  }
  
  // 重写父类方法
  getInfo(): string {
    // 使用 super 调用父类方法
    return `${super.getInfo()} - Doors: ${this.doors}`;
  }
  
  // 调用父类方法
  accelerate(amount: number): void {
    super.accelerate(amount);
    console.log(`Car with ${this.doors} doors is accelerating`);
  }
}

const car = new Car("Toyota", 4);
console.log(car.getInfo());  // "Toyota - Speed: 0 km/h - Doors: 4"
car.accelerate(50);          // "Toyota accelerated to 50 km/h" "Car with 4 doors is accelerating"

构造函数继承

基本构造函数继承

子类必须调用父类的构造函数(如果父类有构造函数),使用 super() 调用:

typescript
class Person {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  
  introduce(): void {
    console.log(`Hi, I'm ${this.name}, ${this.age} years old.`);
  }
}

class Student extends Person {
  studentId: string;
  grade: string;
  
  constructor(name: string, age: number, studentId: string, grade: string) {
    // 必须先调用 super(),然后才能使用 this
    super(name, age);
    this.studentId = studentId;
    this.grade = grade;
  }
  
  study(): void {
    console.log(`${this.name} is studying for grade ${this.grade}`);
  }
  
  // 重写父类方法
  introduce(): void {
    super.introduce();  // 调用父类方法
    console.log(`I'm a student with ID: ${this.studentId}`);
  }
}

const student = new Student("Alice", 20, "S001", "A");
student.introduce();  // "Hi, I'm Alice, 20 years old." "I'm a student with ID: S001"
student.study();       // "Alice is studying for grade A"

注意

在子类构造函数中,必须先调用 super() 才能使用 this 关键字。这是 TypeScript 的强制要求。

构造函数参数传递

子类可以将参数传递给父类构造函数:

typescript
class Employee {
  protected id: number;
  protected name: string;
  protected department: string;
  
  constructor(id: number, name: string, department: string) {
    this.id = id;
    this.name = name;
    this.department = department;
  }
  
  getInfo(): string {
    return `Employee ${this.id}: ${this.name} (${this.department})`;
  }
}

class Manager extends Employee {
  private teamSize: number;
  
  constructor(id: number, name: string, department: string, teamSize: number) {
    // 将参数传递给父类构造函数
    super(id, name, department);
    this.teamSize = teamSize;
  }
  
  getInfo(): string {
    return `${super.getInfo()} - Team Size: ${this.teamSize}`;
  }
  
  manageTeam(): void {
    console.log(`${this.name} is managing a team of ${this.teamSize} people`);
  }
}

const manager = new Manager(1, "John", "Engineering", 5);
console.log(manager.getInfo());  // "Employee 1: John (Engineering) - Team Size: 5"
manager.manageTeam();            // "John is managing a team of 5 people"

方法重写

子类可以重写(override)父类的方法,提供自己的实现:

typescript
class Shape {
  protected color: string;
  
  constructor(color: string) {
    this.color = color;
  }
  
  // 父类方法
  getArea(): number {
    return 0;  // 默认实现
  }
  
  getInfo(): string {
    return `Shape with color ${this.color}`;
  }
}

class Circle extends Shape {
  private radius: number;
  
  constructor(color: string, radius: number) {
    super(color);
    this.radius = radius;
  }
  
  // 重写父类方法
  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
  
  // 重写父类方法,同时调用父类方法
  getInfo(): string {
    return `${super.getInfo()}, Area: ${this.getArea().toFixed(2)}`;
  }
}

class Rectangle extends Shape {
  private width: number;
  private height: number;
  
  constructor(color: string, width: number, height: number) {
    super(color);
    this.width = width;
    this.height = height;
  }
  
  // 重写父类方法
  getArea(): number {
    return this.width * this.height;
  }
  
  // 重写父类方法
  getInfo(): string {
    return `${super.getInfo()}, Area: ${this.getArea()}`;
  }
}

// 使用示例
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);

console.log(circle.getInfo());     // "Shape with color red, Area: 78.54"
console.log(rectangle.getInfo());  // "Shape with color blue, Area: 24"

访问修饰符与继承

protected 成员

protected 成员可以在子类中访问,但不能在类外部访问:

typescript
class Base {
  public publicProp: string = "public";
  protected protectedProp: string = "protected";
  private privateProp: string = "private";
  
  public getPrivateProp(): string {
    return this.privateProp;  // 类内部可以访问 private
  }
}

class Derived extends Base {
  // 可以访问 protected 成员
  public getProtectedProp(): string {
    return this.protectedProp;  // ✅ 子类可以访问 protected
  }
  
  // 不能访问 private 成员
  // public getPrivatePropDirect(): string {
  //   return this.privateProp;  // ❌ 错误:子类不能访问父类的 private 成员
  // }
}

const derived = new Derived();
console.log(derived.publicProp);        // ✅ "public"
console.log(derived.getProtectedProp()); // ✅ "protected"
// console.log(derived.protectedProp);   // ❌ 错误:外部不能访问 protected
// console.log(derived.privateProp);     // ❌ 错误:外部不能访问 private
console.log(derived.getPrivateProp());   // ✅ "private"(通过父类方法)

方法可见性

子类可以改变方法的可见性,但不能降低可见性(例如,不能将 public 改为 private):

typescript
class Base {
  protected method(): void {
    console.log("Base method");
  }
}

class Derived extends Base {
  // ✅ 可以将 protected 改为 public(提高可见性)
  public method(): void {
    super.method();
    console.log("Derived method");
  }
  
  // ❌ 不能将 protected 改为 private(降低可见性)
  // private method(): void { ... }
}

const derived = new Derived();
derived.method();  // ✅ 现在可以从外部访问

多级继承

TypeScript 支持多级继承,一个类可以继承另一个已经继承的类:

typescript
// 第一级:基类
class Animal {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
  
  eat(): void {
    console.log(`${this.name} is eating`);
  }
}

// 第二级:继承 Animal
class Mammal extends Animal {
  bodyTemperature: number;
  
  constructor(name: string, bodyTemperature: number = 37) {
    super(name);
    this.bodyTemperature = bodyTemperature;
  }
  
  breathe(): void {
    console.log(`${this.name} is breathing`);
  }
}

// 第三级:继承 Mammal
class Dog extends Mammal {
  breed: string;
  
  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }
  
  bark(): void {
    console.log(`${this.name} (${this.breed}) barks: Woof!`);
  }
}

// 使用示例
const dog = new Dog("Buddy", "Golden Retriever");
dog.eat();    // "Buddy is eating"(来自 Animal)
dog.breathe(); // "Buddy is breathing"(来自 Mammal)
dog.bark();   // "Buddy (Golden Retriever) barks: Woof!"(来自 Dog)

使用示例

示例 1:图形系统

typescript
// 基类:图形
class Shape {
  protected x: number;
  protected y: number;
  protected color: string;
  
  constructor(x: number, y: number, color: string) {
    this.x = x;
    this.y = y;
    this.color = color;
  }
  
  move(dx: number, dy: number): void {
    this.x += dx;
    this.y += dy;
    console.log(`Shape moved to (${this.x}, ${this.y})`);
  }
  
  draw(): void {
    console.log(`Drawing a shape at (${this.x}, ${this.y}) with color ${this.color}`);
  }
  
  getArea(): number {
    return 0;
  }
}

// 圆形
class Circle extends Shape {
  private radius: number;
  
  constructor(x: number, y: number, color: string, radius: number) {
    super(x, y, color);
    this.radius = radius;
  }
  
  draw(): void {
    console.log(`Drawing a circle at (${this.x}, ${this.y}) with radius ${this.radius}`);
  }
  
  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
  
  getCircumference(): number {
    return 2 * Math.PI * this.radius;
  }
}

// 矩形
class Rectangle extends Shape {
  private width: number;
  private height: number;
  
  constructor(x: number, y: number, color: string, width: number, height: number) {
    super(x, y, color);
    this.width = width;
    this.height = height;
  }
  
  draw(): void {
    console.log(`Drawing a rectangle at (${this.x}, ${this.y}) with size ${this.width}x${this.height}`);
  }
  
  getArea(): number {
    return this.width * this.height;
  }
  
  getPerimeter(): number {
    return 2 * (this.width + this.height);
  }
}

// 使用示例
const circle = new Circle(10, 20, "red", 5);
const rectangle = new Rectangle(30, 40, "blue", 8, 6);

circle.draw();           // "Drawing a circle at (10, 20) with radius 5"
console.log(circle.getArea());  // 78.53981633974483

rectangle.draw();       // "Drawing a rectangle at (30, 40) with size 8x6"
console.log(rectangle.getArea()); // 48

circle.move(5, 10);     // "Shape moved to (15, 30)"

示例 2:员工管理系统

typescript
// 基类:员工
class Employee {
  protected id: number;
  protected name: string;
  protected salary: number;
  
  constructor(id: number, name: string, salary: number) {
    this.id = id;
    this.name = name;
    this.salary = salary;
  }
  
  getInfo(): string {
    return `Employee ${this.id}: ${this.name}`;
  }
  
  calculateSalary(): number {
    return this.salary;
  }
  
  work(): void {
    console.log(`${this.name} is working`);
  }
}

// 开发人员
class Developer extends Employee {
  private programmingLanguage: string;
  
  constructor(id: number, name: string, salary: number, programmingLanguage: string) {
    super(id, name, salary);
    this.programmingLanguage = programmingLanguage;
  }
  
  work(): void {
    console.log(`${this.name} is coding in ${this.programmingLanguage}`);
  }
  
  debug(): void {
    console.log(`${this.name} is debugging code`);
  }
  
  getInfo(): string {
    return `${super.getInfo()} - Developer (${this.programmingLanguage})`;
  }
}

// 经理
class Manager extends Employee {
  private teamSize: number;
  private bonus: number;
  
  constructor(id: number, name: string, salary: number, teamSize: number, bonus: number) {
    super(id, name, salary);
    this.teamSize = teamSize;
    this.bonus = bonus;
  }
  
  work(): void {
    console.log(`${this.name} is managing a team of ${this.teamSize} people`);
  }
  
  calculateSalary(): number {
    return this.salary + this.bonus;
  }
  
  getInfo(): string {
    return `${super.getInfo()} - Manager (Team: ${this.teamSize})`;
  }
  
  conductMeeting(): void {
    console.log(`${this.name} is conducting a team meeting`);
  }
}

// 使用示例
const developer = new Developer(1, "Alice", 8000, "TypeScript");
const manager = new Manager(2, "Bob", 12000, 5, 3000);

console.log(developer.getInfo());  // "Employee 1: Alice - Developer (TypeScript)"
console.log(developer.calculateSalary()); // 8000
developer.work();                   // "Alice is coding in TypeScript"
developer.debug();                  // "Alice is debugging code"

console.log(manager.getInfo());     // "Employee 2: Bob - Manager (Team: 5)"
console.log(manager.calculateSalary()); // 15000
manager.work();                     // "Bob is managing a team of 5 people"
manager.conductMeeting();           // "Bob is conducting a team meeting"

示例 3:支付系统

typescript
// 基类:支付方式
abstract class PaymentMethod {
  protected amount: number;
  protected currency: string;
  
  constructor(amount: number, currency: string = "USD") {
    this.amount = amount;
    this.currency = currency;
  }
  
  abstract process(): boolean;
  
  getAmount(): number {
    return this.amount;
  }
  
  getCurrency(): string {
    return this.currency;
  }
}

// 信用卡支付
class CreditCardPayment extends PaymentMethod {
  private cardNumber: string;
  private cardHolder: string;
  
  constructor(amount: number, cardNumber: string, cardHolder: string, currency: string = "USD") {
    super(amount, currency);
    this.cardNumber = cardNumber;
    this.cardHolder = cardHolder;
  }
  
  process(): boolean {
    console.log(`Processing credit card payment of ${this.currency} ${this.amount} for ${this.cardHolder}`);
    // 模拟支付处理
    return true;
  }
  
  getInfo(): string {
    return `Credit Card: ****${this.cardNumber.slice(-4)} - ${this.cardHolder}`;
  }
}

// 支付宝支付
class AlipayPayment extends PaymentMethod {
  private accountId: string;
  
  constructor(amount: number, accountId: string, currency: string = "CNY") {
    super(amount, currency);
    this.accountId = accountId;
  }
  
  process(): boolean {
    console.log(`Processing Alipay payment of ${this.currency} ${this.amount} for account ${this.accountId}`);
    // 模拟支付处理
    return true;
  }
  
  getInfo(): string {
    return `Alipay: ${this.accountId}`;
  }
}

// 使用示例
const creditPayment = new CreditCardPayment(100, "1234567890123456", "John Doe");
const alipayPayment = new AlipayPayment(500, "alice@example.com");

console.log(creditPayment.getInfo());  // "Credit Card: ****3456 - John Doe"
creditPayment.process();                // "Processing credit card payment of USD 100 for John Doe"

console.log(alipayPayment.getInfo());  // "Alipay: alice@example.com"
alipayPayment.process();                // "Processing Alipay payment of CNY 500 for account alice@example.com"

类型检查示例

常见错误

typescript
// ❌ 错误:忘记调用 super()
class Child extends Parent {
  constructor() {
    // 错误:必须先调用 super()
    this.property = "value";
    // Error: 'super' must be called before accessing 'this' in the constructor of a derived class
  }
}

// ❌ 错误:降低方法可见性
class Base {
  public method(): void {}
}

class Derived extends Base {
  // 错误:不能将 public 改为 private
  private method(): void {}
  // Error: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'
}

// ❌ 错误:访问父类的 private 成员
class Base {
  private prop: string = "private";
}

class Derived extends Base {
  public getProp(): string {
    return this.prop;  // 错误:不能访问父类的 private 成员
    // Error: Property 'prop' is private and only accessible within class 'Base'
  }
}

正确写法

typescript
// ✅ 正确:先调用 super()
class Parent {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
}

class Child extends Parent {
  age: number;
  
  constructor(name: string, age: number) {
    super(name);  // 先调用 super()
    this.age = age;  // 然后才能使用 this
  }
}

// ✅ 正确:提高方法可见性
class Base {
  protected method(): void {}
}

class Derived extends Base {
  public method(): void {  // 可以将 protected 改为 public
    super.method();
  }
}

// ✅ 正确:通过公共方法访问父类私有成员
class Base {
  private prop: string = "private";
  
  public getProp(): string {
    return this.prop;
  }
}

class Derived extends Base {
  public accessProp(): string {
    return this.getProp();  // 通过父类的公共方法访问
  }
}

注意事项

提示

  • 使用 extends 关键字声明继承关系
  • 子类构造函数必须调用 super(),且必须在访问 this 之前调用
  • 使用 super 关键字访问父类的构造函数、方法和属性
  • 子类可以重写父类的方法,提供自己的实现
  • 子类可以添加新的属性和方法
  • 子类可以访问父类的 publicprotected 成员
  • 子类可以提高方法的可见性(如将 protected 改为 public),但不能降低

注意

  • 子类不能访问父类的 private 成员
  • 子类不能降低方法的可见性(如将 public 改为 private
  • 在子类构造函数中,必须先调用 super() 才能使用 this
  • TypeScript 不支持多重继承(一个类不能同时继承多个类)
  • 如果父类有构造函数,子类必须调用它

重要

  • 继承是面向对象编程的核心概念,理解继承对于学习多态、抽象类等高级概念非常重要
  • 合理使用继承可以避免代码重复,提高代码的可维护性
  • 过度使用继承可能导致代码耦合,考虑使用组合(composition)作为替代方案
  • 继承关系应该反映"是一个"(is-a)的关系,而不是"有一个"(has-a)的关系

信息

继承的优势

  • 代码复用:子类可以复用父类的代码
  • 层次结构:建立清晰的类层次关系
  • 多态性:支持多态,提高代码的灵活性
  • 扩展性:易于扩展和维护

继承的应用场景

  • 建立领域模型的层次结构(如 Animal -> Mammal -> Dog)
  • 实现代码复用和共享
  • 实现多态行为
  • 扩展现有类的功能

继承 vs 组合

  • 继承:表示"是一个"关系(Dog is an Animal)
  • 组合:表示"有一个"关系(Car has an Engine)
  • 当需要"是一个"关系时使用继承,当需要"有一个"关系时使用组合

相关链接

基于 VitePress 构建