Event Bus et le pattern Observer en TypeScript : Implémentation et exemples

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

Event Bus et le pattern Observer en TypeScript



Le pattern Observer (Observateur) et son implémentation pratique — l'Event Bus (Bus d'événements) — sont des outils fondamentaux pour construire des applications faiblement couplées et évolutives. Dans cet article, nous allons détailler comment implémenter un Event Bus en TypeScript, en utilisant un typage strict, et examiner son utilisation dans des projets éducatifs.



Qu'est-ce que le pattern Observer ?

Le pattern Observer définit une relation "un à plusieurs" entre les objets. Lorsqu'un objet (l'éditeur, subject) modifie son état, tous les objets qui en dépendent (les observateurs, observers) sont automatiquement notifiés et mis à jour. Cela permet d'éviter un couplage rigide entre les composants.



Qu'est-ce qu'un Event Bus ?

Un Event Bus (bus d'événements) est un mécanisme centralisé qui implémente le pattern Observer. Au lieu d'abonner directement les observateurs à l'éditeur, tous les composants communiquent via un bus unique. Un composant peut :

  • S'abonner (subscribe/listen) à un événement spécifique.
  • Publier (emit/publish) un événement, qui sera transmis à tous les abonnés.
  • Se désabonner (unsubscribe) d'un événement lorsque ce n'est plus nécessaire.

Cette approche est idéale pour :

  • L'interaction entre des modules qui ne doivent pas se connaître.
  • Le traitement des actions utilisateur (clics, saisie de données).
  • La réaction aux changements d'état de l'application (chargement de données, erreurs).


Implémentation d'un Event Bus en TypeScript

TypeScript ajoute un système de types puissant, ce qui rend l'Event Bus sûr et prévisible. Nous allons créer un bus typé, où chaque événement possède un ensemble d'arguments strictement défini.



1. Définition des types d'événements

Commençons par définir une interface pour mapper les noms d'événements et leurs arguments :

// Définissons une carte d'événements : clé = nom de l'événement, valeur = type des argumentsinterface EventMap {    'user:login': { userId: string; timestamp: number };    'data:loaded': { items: string[] };    'error:occurred': { message: string; code: number };    // On peut ajouter n'importe quel autre événement}


2. Création de la classe EventBus

Créons maintenant la classe elle-même, en utilisant les génériques pour un typage strict :

type Listener<T> = (payload: T) => void;

class EventBus<T extends Record<string, unknown>> { private listeners: Map<keyof T, Set<Listener<unknown>>> = new Map();

// Abonnement à un événement subscribe<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } const currentListeners = this.listeners.get(event)!; currentListeners.add(listener as Listener<unknown>);

// Retourne une fonction pour se désabonner return () => { currentListeners.delete(listener as Listener<unknown>); }; }

// Publication d'un événement emit<K extends keyof T>(event: K, payload: T[K]): void { const currentListeners = this.listeners.get(event); if (currentListeners) { currentListeners.forEach((listener) => { listener(payload); }); } }

// Désabonnement de tous les événements (utile pour le nettoyage) clear(): void { this.listeners.clear(); }}

// Créons une instance du bus avec notre carte d'événementsexport const appEventBus = new EventBus<EventMap>();


3. Exemple d'utilisation

Voyons comment différents modules peuvent interagir via l'Event Bus :



// Module d'authentificationclass AuthService {    login(userId: string) {        // ... logique de connexion ...        appEventBus.emit('use                    

Recommendations