Injeção de Dependências em TypeScript: um guia simples para iniciantes

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

Dependency Injection em TypeScript: para iniciantes

\n\n

No mundo do desenvolvimento de software, o termo Dependency Injection (DI), ou injeção de dependências, é frequentemente encontrado. Para um desenvolvedor iniciante em TypeScript, isso pode parecer assustador, mas na verdade é um padrão simples e poderoso que torna seu código mais limpo, testável e fácil de manter. Neste artigo, vamos entender o que é DI, por que é necessário e como aplicá-lo na prática com TypeScript.

\n\n

O que é Dependency Injection?

\n\n

Imagine que você está montando um computador. Em vez de embutir o processador diretamente na placa-mãe na fábrica, você deixa um slot onde pode encaixar qualquer processador compatível. Dependency Injection funciona com o mesmo princípio: em vez de a classe criar suas próprias dependências (como um banco de dados ou um cliente HTTP), ela as recebe de fora.

\n\n

Em termos simples, DI é quando um objeto não cria seus "ajudantes" internamente, mas os recebe prontos através do construtor ou de um método.

\n\n

Problema sem DI: Alto acoplamento

\n\n

Vamos considerar um exemplo comum em projetos reais:

\n\n
class UserService {\n    private database: MySQLDatabase;\n\n    constructor() {\n        this.database = new MySQLDatabase(); // Alto acoplamento!\n    }\n\n    getUser(id: number) {\n        return this.database.findUserById(id);\n    }\n}
\n\n

Aqui, UserService está fortemente acoplado a MySQLDatabase. Se você quiser migrar para PostgreSQL ou escrever testes unitários com um banco de dados fictício, precisará reescrever toda a classe. Isso viola o princípio Open-Closed do SOLID.

\n\n

Solução com DI: Flexibilidade e testabilidade

\n\n

Agora, vamos reescrever a mesma classe usando DI:

\n\n
interface Database {\n    findUserById(id: number): User | null;\n}\n\nclass UserService {\n    constructor(private database: Database) {} // Dependência passada externamente\n\n    getUser(id: number) {\n        return this.database.findUserById(id);\n    }\n}\n\n// Uso\nconst mySQL = new MySQLDatabase();\nconst service = new UserService(mySQL); // Injetamos a dependência
\n\n

Agora, UserService não sabe com qual banco de dados está trabalhando. Você pode facilmente substituir MySQLDatabase por PostgreSQLDatabase ou MockDatabase para testes.

\n\n

Principais formas de injeção de dependências

\n\n

Em TypeScript, existem três formas principais de injetar dependências. Vamos analisar cada uma delas.

\n\n

1. Injeção pelo construtor (Constructor Injection)

\n\n

Esta é a forma mais comum e recomendada. As dependências são passadas através do construtor da classe.

\n\n
class Logger {\n    log(message: string) {\n        console.log(`[LOG]: ${message}`);\n    }\n}\n\nclass OrderService {\n    constructor(private logger: Logger) {}\n\n    createOrder(data: any) {\n        this.logger.log('Pedido criado');\n        // lógica de criação do pedido\n    }\n}\n\nconst logger = new Logger();\nconst orderService = new OrderService(logger);
\n\n

2. Injeção por propriedade (Property Injection)

\n\n

A dependência é atribuída diretamente a uma propriedade do objeto após sua criação. É menos usada, pois pode levar à inicialização incompleta do objeto.

\n\n
class EmailService {\n    sendEmail(to: string, message: string) {\n        console.log(`Email enviado para ${to}: ${message}`);\n    }\n}\n\nclass NotificationService {\n    emailService!: EmailService; // Afirmação de tipo\n\n    notifyUser(user: string) {\n        this.emailService.sendEmail(user, 'Olá!');\n    }\n}\n\nconst notification = new NotificationService();\nnotification.emailService = new EmailService(); // Definindo a propriedade\nnotification.notifyUser('user@example.com');
\n\n

3. Injeção por método (Method Injection)

\n\

Blogs

Book Recommendations