Injection de dépendances (Dependency Injection) en TypeScript : Guide complet

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction à Dependency Injection en TypeScript



Dependency Injection (DI) est l'un des modèles de conception clés qui permet de créer des applications faiblement couplées, testables et faciles à maintenir. Dans le contexte de TypeScript, grâce au typage strict et aux possibilités des décorateurs, l'implémentation de DI devient particulièrement élégante et sécurisée.



Dans cet article, nous allons voir ce qu'est Dependency Injection, comment l'implémenter en TypeScript, et quels outils peuvent simplifier ce processus. Vous découvrirez comment DI aide à lutter contre les couplages rigides entre les classes et rend votre code plus flexible.



Qu'est-ce que Dependency Injection et pourquoi en a-t-on besoin ?



Dependency Injection (injection de dépendances) est un modèle dans lequel un objet reçoit ses dépendances de l'extérieur, plutôt que de les créer lui-même. Cela implémente le principe Inversion of Control (IoC) — l'inversion de contrôle.



Problème sans DI :

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

class UserService { private logger: Logger;

constructor() { this.logger = new Logger(); // Couplage rigide }

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


Dans cet exemple, UserService est fortement couplé à une implémentation spécifique de Logger. Si nous voulons remplacer le logger par, disons, un logger fichier ou distant, nous devrons modifier le code de UserService.



Solution avec 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 { // Écriture dans un fichier }}

class UserService { constructor(private logger: ILogger) {} // Dépendance injectée

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

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


Avantages de DI :

  • Testabilité — il est facile de remplacer les dépendances réelles par des objets mock.
  • Flexibilité — on peut modifier le comportement du système sans changer le code des classes.
  • Séparation des responsabilités — les classes ne gèrent pas la création de leurs dépendances.
  • Réutilisabilité — les dépendances peuvent être utilisées dans différentes parties de l'application.


Implémentation manuelle de Dependency Injection en TypeScript



La façon la plus simple est d'injecter les dépendances via le constructeur ou les propriétés. TypeScript, avec ses interfaces et ses modificateurs d'accès, rend cela très pratique.



Injection par constructeur (Constructor Injection)

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

class UserRepository implements IRepository<User> { async getAll(): Promise<User[]> { // Logique réelle de travail avec la base de données 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(); }}

// Création du graphe de dépendancesconst logger = new ConsoleLogger();const repository = new UserRepository();const controller = new UserController(repository, logger);


Injection par propriété (Property Injection)

class EmailService {    private sender!: ISender;

setSender(sender: ISender): void {

Blogs

Book Recommendations