Inyección de Dependencias en TypeScript: Guía completa con ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Qué es Dependency Injection en TypeScript y para qué sirve?



Dependency Injection (DI) es un patrón de diseño que implementa el principio de inversión de control (Inversion of Control, IoC). En lugar de que un objeto cree sus propias dependencias, estas se "inyectan" desde el exterior. En TypeScript, este patrón es especialmente útil gracias al tipado estricto y las capacidades de los decoradores.



Imagina que estás construyendo una aplicación para una tienda online. Tienes una clase OrderService que debe enviar notificaciones. Sin DI, estaría rígidamente vinculada a un método específico de envío (por ejemplo, Email). Con DI, puedes cambiar fácilmente de Email a SMS o notificaciones Push sin modificar el código de OrderService.



¿Por qué usar DI en TypeScript?

  • Testeabilidad: Es fácil reemplazar servicios reales por objetos mock en pruebas unitarias.
  • Flexibilidad: El sistema se vuelve modular, las dependencias se pueden cambiar sin modificar el código cliente.
  • Simplificación del mantenimiento: Cada clase se encarga solo de su lógica de negocio, no de crear otros objetos.
  • Seguridad de tipos: TypeScript verifica los tipos en tiempo de compilación, lo que reduce la cantidad de errores en tiempo de ejecución.


Implementación básica de DI en TypeScript sin frameworks



Comencemos con un ejemplo simple. Crearemos una interfaz para el servicio de notificaciones y dos implementaciones.



// Definimos la interfazinterface INotificationService {    send(message: string): void;}

// Implementación para Emailclass EmailNotificationService implements INotificationService { send(message: string): void { console.log(`Enviando Email: ${message}`); }}

// Implementación para SMSclass SMSNotificationService implements INotificationService { send(message: string): void { console.log(`Enviando SMS: ${message}`); }}

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

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

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


En este ejemplo, OrderService no sabe qué servicio de notificaciones se está utilizando. Simplemente llama al método send a través de la interfaz.



DI avanzado: Creación de un contenedor IoC simple



Para proyectos grandes, la inyección manual de dependencias se vuelve engorrosa. Aquí es donde entra en juego el contenedor IoC. Veamos cómo implementarlo usando decoradores de TypeScript.



Paso 1: Habilite el soporte para decoradores en tsconfig.json

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


Paso 2: Cree el contenedor 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(`El servicio "${token}" no está registrado`); }

// Obtenemos los tipos de los parámetros del constructor mediante reflexión const paramTypes: Constructor<any>[] = Reflect.getMetadata('design:paramtypes', Implementation) || [];

// Resolvemos las dependencias recursivamente const dependencies = param

Blogs

Book Recommendations