tsyringe para TypeScript: guia completo com exemplos práticos
No desenvolvimento moderno com TypeScript, o gerenciamento de dependências é uma das tarefas essenciais, especialmente na construção de aplicações escaláveis e testáveis. A biblioteca tsyringe é um contêiner de injeção de dependência (Dependency Injection, DI) leve e poderoso, totalmente baseado em decorators do TypeScript. Ela permite que desenvolvedores descrevam de forma declarativa as dependências entre componentes, resolvam-nas automaticamente e gerenciem o ciclo de vida dos objetos.
Por que usar tsyringe? Em projetos grandes, criar manualmente instâncias de classes e passá-las para construtores gera um código altamente acoplado, dificulta os testes e torna a substituição de implementações um desafio. A tsyringe resolve esses problemas fornecendo um registro centralizado (contêiner) que sabe como criar e fornecer dependências. Você simplesmente indica quais classes ou tokens devem ser registrados, e o contêiner os injeta automaticamente onde são necessários. Isso torna o código mais limpo, modular e simplifica significativamente a escrita de testes unitários.
A biblioteca utiliza ativamente os decorators do TypeScript (@injectable(), @inject()), tornando a sintaxe intuitiva para desenvolvedores familiarizados com Angular ou NestJS. A tsyringe não requer configuração complexa, funciona "pronta para uso" com versões modernas do TypeScript e suporta diferentes estratégias de registro (singleton, transient, scoped). Neste artigo, vamos detalhar todos os aspectos do uso da tsyringe, desde a instalação até cenários de uso avançados.
Instalação
Para instalar a tsyringe no seu projeto TypeScript, execute o seguinte comando usando npm:
npm install tsyringe reflect-metadata
Após a instalação, é necessário garantir que o suporte a decorators esteja configurado no seu projeto. Para isso, no arquivo tsconfig.json, as seguintes opções devem estar ativadas:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Além disso, no ponto de entrada da sua aplicação (por exemplo, index.ts), é necessário importar o reflect-metadata uma única vez para habilitar os metadados dos decorators:
import "reflect-metadata";
// ... resto do código
Início rápido: exemplo mínimo funcional
Vamos criar uma aplicação simples que demonstra o mecanismo básico da tsyringe. Teremos um serviço para trabalhar com banco de dados, que será injetado em um controller.
import "reflect-metadata";
import { container, injectable, inject } from "tsyringe";
// 1. Define o serviço que será injetado
@injectable()
class DatabaseService {
getData(): string {
return "Dados do banco de dados";
}
}
// 2. Define o controller que depende do DatabaseService
@injectable()
class UserController {
constructor(
@inject(DatabaseService) private dbService: DatabaseService
) {}
getUserData(): string {
return this.dbService.getData();
}
}
// 3. Registra as classes no contêiner (neste caso, não é obrigatório,
// pois a tsyringe pode resolver automaticamente classes registradas)
container.registerSingleton(DatabaseService);
container.registerSingleton(UserController);
// 4. Usa o contêiner para obter uma instância do controller
const controller = container.resolve(UserController);
console.log(controller.getUserData()); // Exibe: "Dados do banco de dados"
Este exemplo mostra três etapas principais: marcar a classe como @injectable(), indicar as dependências com @inject() no construtor e obter a instância através de container.resolve(). Observe que, se a classe for registrada com o decorator