继承

继承是面向对象编程的三大特性之一,它允许子类复用父类的代码,并在此基础上进行扩展。TypeScript 使用 extends 关键字实现单继承,子类可以获得父类的所有属性和方法。

基本继承

子类使用 extends 关键字继承父类,自动获得父类的所有公共成员。

class Animal {
  name: string

  constructor(name: string) {
    this.name = name
  }

  speak(): void {
    console.log(`${this.name} 发出声音`)
  }
}

class Cat extends Animal {
  meow(): void {
    console.log(`${this.name} 喵喵叫`)
  }
}

const cat = new Cat("小白")
cat.speak()
cat.meow()

Cat 类继承了 Animal 类,拥有了 name 属性和 speak 方法,同时添加了自己的 meow 方法。

super 关键字

子类构造函数必须调用 super(),这会执行父类的构造函数。super 也可以用来调用父类的方法。

class Vehicle {
  brand: string

  constructor(brand: string) {
    this.brand = brand
  }

  start(): void {
    console.log(`${this.brand} 车辆启动`)
  }
}

class Car extends Vehicle {
  model: string

  constructor(brand: string, model: string) {
    super(brand)
    this.model = model
  }

  start(): void {
    super.start()
    console.log(`${this.brand} ${this.model} 准备出发`)
  }
}

const car = new Car("丰田", "凯美瑞")
car.start()

子类构造函数中,super() 必须在访问 this 之前调用。子类方法中使用 super.method() 可以调用父类的同名方法。

方法重写

子类可以重新定义父类的方法,实现不同的行为。这叫做方法重写。

class Shape {
  name: string = "形状"

  getArea(): number {
    return 0
  }

  describe(): string {
    return `这是一个${this.name},面积是 ${this.getArea()}`
  }
}

class Circle extends Shape {
  radius: number

  constructor(radius: number) {
    super()
    this.name = "圆形"
    this.radius = radius
  }

  getArea(): number {
    return Math.PI * this.radius * this.radius
  }
}

class Rectangle extends Shape {
  width: number
  height: number

  constructor(width: number, height: number) {
    super()
    this.name = "矩形"
    this.width = width
    this.height = height
  }

  getArea(): number {
    return this.width * this.height
  }
}

const circle = new Circle(5)
const rectangle = new Rectangle(10, 20)

console.log(circle.describe())
console.log(rectangle.describe())

CircleRectangle 都重写了 getArea 方法,各自计算自己的面积。describe 方法继承自父类,但调用的 getArea 是子类的版本。

属性继承

子类继承父类的属性,可以添加新的属性,也可以重新定义属性类型。

class User {
  name: string
  email: string

  constructor(name: string, email: string) {
    this.name = name
    this.email = email
  }
}

class Admin extends User {
  role: string = "管理员"
  permissions: string[]

  constructor(name: string, email: string, permissions: string[]) {
    super(name, email)
    this.permissions = permissions
  }

  hasPermission(permission: string): boolean {
    return this.permissions.includes(permission)
  }
}

const admin = new Admin("张三", "admin@example.com", ["read", "write", "delete"])
console.log(admin.name)
console.log(admin.role)
console.log(admin.hasPermission("write"))

Admin 类继承了 Usernameemail 属性,添加了 rolepermissions 属性。

继承链

继承可以形成链条,一个类继承另一个类,后者又可以继承其他类。

class Entity {
  id: number

  constructor(id: number) {
    this.id = id
  }
}

class Person extends Entity {
  name: string

  constructor(id: number, name: string) {
    super(id)
    this.name = name
  }
}

class Employee extends Person {
  department: string

  constructor(id: number, name: string, department: string) {
    super(id, name)
    this.department = department
  }

  introduce(): string {
    return `我是 ${this.name},工号 ${this.id},在 ${this.department} 工作`
  }
}

const emp = new Employee(1001, "李四", "技术部")
console.log(emp.introduce())

Employee 继承 PersonPerson 继承 Entity,形成了三层继承链。Employee 实例拥有所有祖先类的属性。

类型兼容性

子类实例可以赋值给父类类型的变量,这是多态的基础。

class Animal {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

class Dog extends Animal {
  breed: string

  constructor(name: string, breed: string) {
    super(name)
    this.breed = breed
  }
}

function printAnimal(animal: Animal): void {
  console.log(`动物名字: ${animal.name}`)
}

const dog = new Dog("小黑", "拉布拉多")
printAnimal(dog)

const animal: Animal = dog
console.log(animal.name)

Dog 实例可以赋值给 Animal 类型的变量,因为 DogAnimal 的子类型。但反过来不行,父类实例不能赋值给子类类型的变量。

继承的局限

TypeScript 只支持单继承,一个类只能有一个直接父类。如果需要组合多个类的功能,可以使用接口或混入。

interface Flyable {
  fly(): void
}

interface Swimmable {
  swim(): void
}

class Bird implements Flyable, Swimmable {
  fly(): void {
    console.log("飞翔")
  }

  swim(): void {
    console.log("游泳")
  }
}

通过实现多个接口,可以让一个类具备多种能力,弥补单继承的限制。

小结

继承是代码复用的重要手段,通过 extends 关键字,子类可以获得父类的属性和方法。子类可以添加新成员,也可以重写父类的方法。合理使用继承可以减少代码重复,构建清晰的类层次结构。但要注意避免过深的继承链,保持代码的简洁和可维护性。