类继承
概述
类继承(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关键字访问父类的构造函数、方法和属性 - 子类可以重写父类的方法,提供自己的实现
- 子类可以添加新的属性和方法
- 子类可以访问父类的
public和protected成员 - 子类可以提高方法的可见性(如将
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)
- 当需要"是一个"关系时使用继承,当需要"有一个"关系时使用组合
相关链接
- 类基础 - 了解类的基础概念
- 访问修饰符 - 详细了解访问修饰符的使用
- 抽象类 - 学习抽象类的概念和使用
- 接口 - 了解接口的定义和使用
- TypeScript 官方文档 - 类继承