0Pricing
TypeScript Academy · Lekcja

Generyczna klasa Event Emitter

Zbuduj od podstaw wielokrotnego użytku, typowany emiter zdarzeń

Generyczna klasa Event Emitter to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

Cel: klasa TypedEmitter z bezpiecznymi typami

Zbudujemy wielokrotnego użytku klasę TypedEmitter<E> generyczną względem mapy zdarzeń E, z w pełni typowanymi metodami on, off i emit.

interface Events { login: { userId: string }; logout: void }
// class TypedEmitter<Events> will manage handlers safely.
console.log("building a typed emitter");

Alias typu handlera

Należy zdefiniować typ handlera parametryzowany nazwą zdarzenia, aby każdy listener otrzymywał właściwy payload.

type Handler<E, K extends keyof E> = (payload: E[K]) => void;
// E[K] is the payload type for event K in map E.
console.log("handler typed by E[K]");

Mapa listeners

Handlery należy przechowywać pod kluczami będącymi nazwami zdarzeń. Typ mapowany zapewnia każdemu kluczowi tablicę poprawnie typowanych handlerów.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
}
console.log("listeners map ready");

Implementowanie on

Metoda on dopisuje handler do tablicy przypisanej do zdarzenia, tworząc tę tablicę przy pierwszym użyciu.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(event: K, handler: (p: E[K]) => void): void {
    (this.listeners[event] ||= []).push(handler);
  }
}
console.log("on implemented");

Implementowanie emit

Metoda emit wyszukuje handlery przypisane do zdarzenia i wywołuje każdy z nich z typowanym payloadem.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, payload: E[K]): void {
    (this.listeners[e] || []).forEach(h => h(payload));
  }
}
console.log("emit implemented");

Implementowanie off

Metoda off usuwa wcześniej zarejestrowany handler, odfiltrowując go z tablicy.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  off<K extends keyof E>(e: K, h: (p: E[K]) => void) {
    this.listeners[e] = (this.listeners[e] || []).filter(x => x !== h);
  }
}
console.log("off implemented");

Składanie klasy w całość

Oto pełna klasa ze wszystkimi trzema metodami, generyczna względem dowolnej dostarczonej mapy zdarzeń.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  off<K extends keyof E>(e: K, h: (p: E[K]) => void) { this.listeners[e] = (this.listeners[e] || []).filter(x => x !== h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
console.log("TypedEmitter complete");

Korzystanie z TypedEmitter

Należy utworzyć instancję z konkretną mapą zdarzeń. Teraz on i emit są w pełni typowane dla tych zdarzeń.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
interface Events { login: { userId: string } }
const bus = new TypedEmitter<Events>();
bus.on("login", p => console.log("hi", p.userId));
bus.emit("login", { userId: "u1" });

Błędy typów są wykrywane

Klasa odrzuca nieznane zdarzenia i niepoprawne payloady w czasie kompilacji, tak samo jak robiły to samodzielne funkcje.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
interface Events { ping: { at: number } }
const bus = new TypedEmitter<Events>();
// bus.emit("pong", { at: 1 }); // Error
bus.emit("ping", { at: 1 });
console.log("typed bus");

Wiele handlerów dla jednego zdarzenia

Ponieważ każde zdarzenie jest mapowane na tablicę, można zarejestrować wiele handlerów, a wszystkie zostaną uruchomione podczas wywołania emit.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
interface Events { tick: { n: number } }
const bus = new TypedEmitter<Events>();
bus.on("tick", p => console.log("a", p.n));
bus.on("tick", p => console.log("b", p.n));
bus.emit("tick", { n: 7 });

Podstawa do dalszego rozwijania

Ta klasa stanowi solidną podstawę. W następnej kolejności dopracujemy wywnioskowywanie argumentów listenerów i dodamy semantykę once.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
const bus = new TypedEmitter<{ go: void }>();
bus.on("go", () => console.log("go!"));
bus.emit("go", undefined as void);

Szybkie sprawdzenie: generyczna klasa emitera

Proszę sprawdzić swoją wiedzę na temat generycznej klasy emitera zdarzeń.

Podsumowanie: generyczna klasa emitera zdarzeń

Zbudowali Państwo klasę TypedEmitter<E> z mapowanym magazynem listenerów oraz w pełni typowanymi metodami on, off i emit, uzyskując wielokrotnego użytku, bezpieczny typowo mechanizm pub/sub dla dowolnej mapy zdarzeń.

class TypedEmitter<E> {
  private listeners: { [K in keyof E]?: Array<(p: E[K]) => void> } = {};
  on<K extends keyof E>(e: K, h: (p: E[K]) => void) { (this.listeners[e] ||= []).push(h); }
  emit<K extends keyof E>(e: K, p: E[K]) { (this.listeners[e] || []).forEach(h => h(p)); }
}
const bus = new TypedEmitter<{ hi: { name: string } }>();
bus.on("hi", p => console.log(p.name));
bus.emit("hi", { name: "Ada" });

Często zadawane pytania

Czy lekcja „Generyczna klasa Event Emitter” jest bezpłatna?

Tak — pełny tekst „Generyczna klasa Event Emitter” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Generyczna klasa Event Emitter”?

Zbuduj od podstaw wielokrotnego użytku, typowany emiter zdarzeń Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?

Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Generyczna klasa Event Emitter”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?

Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Typowanie map zdarzeń
  2. Bezpieczne typowo emit i on
  3. Generyczna klasa Event Emitter
  4. Wnioskowanie argumentów listenerów
← Powrót do TypeScript Academy