1. 从现实世界理解继承与多态
想象你走进一家宠物店,看到各种动物——狗、猫、鸟。虽然它们形态各异,但都有"动物"这个共同特征:会呼吸、需要进食、能够移动。这种关系在编程中就是"继承"(Inheritance)——狗继承自动物类,获得基础能力的同时又可以有自己的独特行为(比如汪汪叫)。
而当你对店员说"请让宠物们发出声音"时,不同动物会有不同反应:狗叫、猫喵、鸟鸣。这就是"多态"(Polymorphism)——同一指令引发不同对象产生符合自身特性的行为。这两个概念构成了面向对象编程(OOP)的支柱,也是TypeScript、Java、C++等语言的核心特性。
关键理解:继承描述的是"是什么"的关系(狗是一种动物),多态解决的是"怎么做"的问题(不同动物用不同方式响应同一指令)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. TypeScript中的类继承实战
2.1 基础继承语法
在TypeScript中,使用extends关键字实现继承。下面是一个动物体系的完整示例:
typescript复制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 {
breed: string;
constructor(name: string, breed: string) {
super(name); // 必须首先调用父类构造函数
this.breed = breed;
}
bark() {
console.log('Woof! Woof!');
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.move(10); // 继承自Animal的方法
myDog.bark(); // Dog特有的方法
2.2 super关键字的三种用法
- 构造函数调用:
super()必须在子类构造函数第一行 - 方法调用:
super.method()调用父类被覆盖的方法 - 属性访问:
super.property访问父类属性(使用较少)
2.3 常见陷阱与解决方案
问题1:遇到Class extends value undefined is not a constructor错误
- 原因:通常是循环依赖或模块导入问题
- 解决方案:
typescript复制// 错误示例: import { Animal } from './animal'; export class Dog extends Animal {} // 正确写法: import type { Animal } from './animal'; // 使用type-only导入 export class Dog extends Animal {}
问题2:忘记调用super()
- TypeScript会强制检查,但JavaScript运行时才会报错
- 最佳实践:在子类构造函数中总是先写super调用
3. 多态的实现与类型系统
3.1 方法重写(Override)
子类可以重新定义父类方法,这是多态的基础:
typescript复制class Animal {
makeSound() {
console.log('Some generic sound');
}
}
class Cat extends Animal {
makeSound() {
console.log('Meow!');
}
}
const animals: Animal[] = [new Animal(), new Cat()];
animals.forEach(animal => animal.makeSound());
// 输出:
// Some generic sound
// Meow!
3.2 抽象类与多态
抽象类强制子类实现特定方法,是设计多态系统的利器:
typescript复制abstract class Shape {
abstract getArea(): number;
printArea() {
console.log(`Area: ${this.getArea()}`);
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
getArea() {
return Math.PI * this.radius ** 2;
}
}
class Square extends Shape {
constructor(private side: number) {
super();
}
getArea() {
return this.side * this.side;
}
}
const shapes: Shape[] = [new Circle(5), new Square(4)];
shapes.forEach(shape => shape.printArea());
3.3 接口实现多态
TypeScript的接口也可以实现多态,更轻量级:
typescript复制interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
console.log('Logging to console');
}
}
class FileLogger implements Loggable {
log() {
console.log('Logging to file');
}
}
function processLoggers(loggers: Loggable[]) {
loggers.forEach(logger => logger.log());
}
4. 高级模式与工程实践
4.1 混入(Mixins)模式
TypeScript通过交叉类型实现多重继承效果:
typescript复制type Constructor = new (...args: any[]) => {};
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false;
activate() {
this.isActive = true;
}
};
}
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
const TimestampedActivatableUser = Activatable(Timestamped(User));
const user = new TimestampedActivatableUser('John');
user.activate();
console.log(user.isActive, user.timestamp);
4.2 多态在框架中的应用
以Express中间件为例,展示多态的实际价值:
typescript复制interface Middleware {
handle(request: any, next: () => void): void;
}
class LoggerMiddleware implements Middleware {
handle(request: any, next: () => void) {
console.log(`Request received at ${new Date()}`);
next();
}
}
class AuthMiddleware implements Middleware {
handle(request: any, next: () => void) {
if (!request.headers.authorization) {
throw new Error('Unauthorized');
}
next();
}
}
function runMiddlewares(middlewares: Middleware[], request: any) {
let index = 0;
function next() {
if (index < middlewares.length) {
const middleware = middlewares[index++];
middleware.handle(request, next);
}
}
next();
}
const middlewares: Middleware[] = [
new LoggerMiddleware(),
new AuthMiddleware()
];
runMiddlewares(middlewares, { headers: {} });
4.3 性能考量与优化
- 方法调用开销:多态调用比静态调用稍慢(现代JS引擎已优化)
- 内存占用:继承层级过深会增加内存使用
- 优化建议:
- 避免超过3层的继承链
- 对性能关键路径考虑使用组合替代继承
- 使用
final类(通过@final装饰器)阻止进一步继承
5. 现代TypeScript中的最佳实践
5.1 使用private/protected修饰符
明确控制访问权限,避免继承滥用:
typescript复制class PaymentProcessor {
private validate() {
// 内部验证逻辑
}
protected processPayment() {
this.validate();
// 支付处理
}
}
class CreditCardProcessor extends PaymentProcessor {
process() {
this.processPayment(); // 可以访问protected方法
// this.validate(); // 错误!private方法不可访问
}
}
5.2 类型守卫与多态
安全地处理不同类型:
typescript复制class Bird {
fly() {
console.log('Flying');
}
}
class Fish {
swim() {
console.log('Swimming');
}
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
5.3 装饰器与元编程
通过装饰器增强继承能力:
typescript复制function logCreation(target: any) {
return class extends target {
constructor(...args: any[]) {
super(...args);
console.log(`Instance of ${target.name} created`);
}
};
}
@logCreation
class ApiClient {
constructor(private baseUrl: string) {}
}
const client = new ApiClient('https://api.example.com');
// 控制台输出: Instance of ApiClient created
在实际项目中,我通常会遵循这些原则:
- 优先使用组合而非继承(但继承在UI组件等场景仍不可替代)
- 保持继承链扁平化(不超过3层)
- 多态方法应该保持相同的行为契约(Liskov替换原则)
- 对核心业务概念使用抽象类,对能力描述使用接口
