Injeção de Dependência em TypeScript: Guia Completo com Exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que é Dependency Injection em TypeScript e por que é necessário?



Dependency Injection (DI) é um padrão de projeto que implementa o princípio de Inversão de Controle (Inversion of Control, IoC). Em vez de um objeto criar suas próprias dependências, elas são "injetadas" externamente. Em TypeScript, esse padrão é especialmente útil devido à tipagem forte e aos recursos de decorators.



Imagine que você está construindo uma aplicação para uma loja online. Você tem a classe OrderService que precisa enviar notificações. Sem DI, ela ficaria rigidamente vinculada a uma forma específica de envio (por exemplo, Email). Com DI, você pode facilmente substituir Email por SMS ou notificações Push sem alterar o código de OrderService.



Por que usar DI em TypeScript?

  • Testabilidade: Facilidade para substituir serviços reais por objetos mock em testes unitários.
  • Flexibilidade: O sistema se torna modular, as dependências podem ser alteradas sem modificar o código cliente.
  • Simplificação da manutenção: Cada classe é responsável apenas pela sua lógica de negócio, e não pela criação de outros objetos.
  • Type safety: TypeScript verifica os tipos em tempo de compilação, o que reduz a quantidade de erros em tempo de execução.


Implementação básica de DI em TypeScript sem frameworks



Vamos começar com um exemplo simples. Criaremos uma interface para o serviço de notificações e duas implementações dela.



// Definindo a interfaceinterface INotificationService {    send(message: string): void;}

// Implementação para Emailclass EmailNotificationService implements INotificationService { send(message: string): void { console.log(`Enviando Email: ${message}`); }}

// Implementação para SMSclass SMSNotificationService implements INotificationService { send(message: string): void { console.log(`Enviando SMS: ${message}`); }}

// Classe que usa DIclass OrderService { constructor(private notificationService: INotificationService) {}

processOrder(orderId: number): void { // Lógica de processamento do pedido this.notificationService.send(`Pedido #${orderId} processado`); }}

// Usoconst emailService = new EmailNotificationService();const orderService = new OrderService(emailService);orderService.processOrder(123); // Enviando Email: Pedido #123 processado


Neste exemplo, OrderService não sabe qual serviço de notificação está sendo usado. Ele simplesmente chama o método send através da interface.



DI Avançado: Criando um container IoC simples



Para projetos grandes, a injeção manual de dependências se torna trabalhosa. É aqui que o container IoC entra em ação. Vamos ver como implementá-lo usando decorators do TypeScript.



Passo 1: Habilite o suporte a decorators no tsconfig.json

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


Passo 2: Crie o container 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(`Serviço "${token}" não registrado`); }

// Obtendo os tipos dos parâmetros do construtor através de reflexão const paramTypes: Constructor<any>[] = Reflect.getMetadata('design:paramtypes', Implementation) || [];

// Resolvendo dependências recursivamente const dependencies = param

Blogs

Book Recommendations