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
- Typowanie map zdarzeń
- Bezpieczne typowo emit i on
- Generyczna klasa Event Emitter
- Wnioskowanie argumentów listenerów