TypeScript中的依赖注入:完整指南与示例

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

什么是 TypeScript 中的依赖注入以及为什么需要它?



Dependency Injection (DI) 是一种设计模式,实现了控制反转(Inversion of Control, IoC)原则。对象不再自己创建依赖,而是从外部"注入"依赖。在 TypeScript 中,由于强类型和装饰器的支持,这种模式特别有用。



想象一下,您正在构建一个在线商店应用程序。您有一个 OrderService 类,它需要发送通知。没有 DI,它将硬编码绑定到特定的发送方式(例如 Email)。使用 DI,您可以轻松地将 Email 替换为 SMS 或 Push 通知,而无需更改 OrderService 的代码。



为什么要在 TypeScript 中使用 DI?

  • 可测试性: 在单元测试中轻松将真实服务替换为 mock 对象。
  • 灵活性: 系统变得模块化,无需更改客户端代码即可更改依赖。
  • 简化维护: 每个类只负责自己的业务逻辑,而不是创建其他对象。
  • 类型安全: TypeScript 在编译时检查类型,减少运行时错误的数量。


在 TypeScript 中无框架的基本 DI 实现



从一个简单的例子开始。为通知服务创建一个接口及其两个实现。



// 定义接口interface INotificationService {    send(message: string): void;}

// Email 实现class EmailNotificationService implements INotificationService { send(message: string): void { console.log(`发送 Email: ${message}`); }}

// SMS 实现class SMSNotificationService implements INotificationService { send(message: string): void { console.log(`发送 SMS: ${message}`); }}

// 使用 DI 的类class OrderService { constructor(private notificationService: INotificationService) {}

processOrder(orderId: number): void { // 订单处理逻辑 this.notificationService.send(`订单 #${orderId} 已处理`); }}

// 使用const emailService = new EmailNotificationService();const orderService = new OrderService(emailService);orderService.processOrder(123); // 发送 Email: 订单 #123 已处理


在这个例子中,OrderService 不知道具体使用哪个通知服务。它只是通过接口调用 send 方法。



高级 DI:创建简单的 IoC 容器



对于大型项目,手动注入依赖变得繁琐。这时 IoC 容器就派上用场了。让我们看看如何使用 TypeScript 装饰器实现它。



步骤 1:在 tsconfig.json 中启用装饰器支持

{    "compilerOptions": {        "experimentalDecorators": true,        "emitDecoratorMetadata": true    }}


步骤 2:创建 DI 容器

// di-container.tstype Constructor<T> = new (...args: any[]) => T;

class DIContainer { private services = new Map<string, any>();

register<T>(token: string, implementation: Constructor<T>): void { this.services.set(token, implementation); }

resolve<T>(token: string): T { const Implementation = this.services.get(token); if (!Implementation) { throw new Error(`服务 "${token}" 未注册`); }

// 通过反射获取构造函数参数类型 const paramTypes: Constructor<any>[] = Reflect.getMetadata('design:paramtypes', Implementation) || [];

// 递归解析依赖 const dependencies = param

博客

书籍推荐