Inyección de Dependencias (Dependency Injection) en TypeScript: Guía completa

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

Introducción a Dependency Injection en TypeScript



Dependency Injection (DI) es uno de los patrones de diseño clave que permite crear aplicaciones débilmente acopladas, testeables y fáciles de mantener. En el contexto de TypeScript, gracias al tipado estricto y las capacidades de los decoradores, la implementación de DI se vuelve especialmente elegante y segura.



En este artículo, veremos qué es Dependency Injection, cómo implementarlo en TypeScript y qué herramientas pueden simplificar este proceso. Aprenderás cómo DI ayuda a combatir los acoplamientos rígidos entre clases y hace que tu código sea más flexible.



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



Dependency Injection (inyección de dependencias) es un patrón en el cual un objeto recibe sus dependencias desde el exterior, en lugar de crearlas por sí mismo. Esto implementa el principio de Inversion of Control (IoC) — inversión de control.



Problema sin DI:

class Logger {    log(message: string): void {        console.log(message);    }}

class UserService { private logger: Logger;

constructor() { this.logger = new Logger(); // Acoplamiento rígido }

createUser(name: string): void { this.logger.log(`User ${name} created`); }}


En este ejemplo, UserService está rígidamente vinculado a una implementación concreta de Logger. Si quisiéramos reemplazar el logger por uno de archivo o remoto, tendríamos que modificar el código de UserService.



Solución con DI:

interface ILogger {    log(message: string): void;}

class ConsoleLogger implements ILogger { log(message: string): void { console.log(message); }}

class FileLogger implements ILogger { log(message: string): void { // Escritura en archivo }}

class UserService { constructor(private logger: ILogger) {} // Dependencia inyectada

createUser(name: string): void { this.logger.log(`User ${name} created`); }}

// Usoconst service = new UserService(new ConsoleLogger());


Ventajas de DI:

  • Testeabilidad — es fácil reemplazar dependencias reales por objetos mock.
  • Flexibilidad — se puede cambiar el comportamiento del sistema sin modificar el código de las clases.
  • Separación de responsabilidades — las clases no se encargan de crear sus propias dependencias.
  • Reutilización — las dependencias pueden usarse en diferentes partes de la aplicación.


Implementación manual de Dependency Injection en TypeScript



La forma más sencilla es inyectar dependencias a través del constructor o propiedades. TypeScript, con sus interfaces y modificadores de acceso, lo hace muy conveniente.



Inyección por constructor (Constructor Injection)

interface IRepository<T> {    getAll(): Promise<T[]>;    getById(id: number): Promise<T | null>;}

class UserRepository implements IRepository<User> { async getAll(): Promise<User[]> { // Lógica real de trabajo con BD return []; }

async getById(id: number): Promise<User | null> { return null; }}

class UserController { constructor( private repository: IRepository<User>, private logger: ILogger ) {}

async getUsers(): Promise<User[]> { this.logger.log('Fetching users...'); return this.repository.getAll(); }}

// Creación del grafo de dependenciasconst logger = new ConsoleLogger();const repository = new UserRepository();const controller = new UserController(repository, logger);


Inyección por propiedades (Property Injection)

class EmailService {    private sender!: ISender;

setSender(sender: ISender): void {

Blogs

Book Recommendations