0Pricing
TypeScript Academy · บทเรียน

คลาสตัวส่งเหตุการณ์แบบเจเนอริก

สร้างตัวส่งเหตุการณ์แบบมีชนิดข้อมูลที่นำกลับมาใช้ซ้ำได้ตั้งแต่ต้น

คลาสตัวส่งเหตุการณ์แบบเจเนอริก เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

เป้าหมาย: คลาส TypedEmitter

เราจะสร้างคลาส TypedEmitter<E> ที่นำกลับมาใช้ใหม่ได้ โดยเป็นแบบทั่วไปเหนือแผนผังเหตุการณ์ E พร้อมเมธอด on off และ emit ที่มีชนิดข้อมูลครบถ้วน

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

นามแฝงชนิดข้อมูลของตัวจัดการ

กำหนดชนิดข้อมูลของตัวจัดการโดยมีพารามิเตอร์เป็นชื่อเหตุการณ์ เพื่อให้ผู้รับฟังแต่ละตัวได้รับข้อมูลประกอบที่ถูกต้อง

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]");

แผนที่ listeners

จัดเก็บตัวจัดการโดยใช้ชื่อเหตุการณ์เป็นคีย์ ชนิดข้อมูลที่แมปจะกำหนดให้อินเทอร์เฟซแต่ละคีย์เป็นอาร์เรย์ของตัวจัดการที่มีชนิดข้อมูลถูกต้อง

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

การใช้งาน on

เมธอด on จะเพิ่มตัวจัดการต่อท้ายอาร์เรย์ของเหตุการณ์นั้น และสร้างอาร์เรย์เมื่อใช้งานครั้งแรก

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");

การใช้งาน emit

เมธอด emit จะค้นหา handlers ของเหตุการณ์ แล้วเรียกใช้แต่ละตัวพร้อมข้อมูลประกอบที่มีชนิดข้อมูล

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");

การใช้งาน off

เมธอด off จะลบตัวจัดการที่ลงทะเบียนไว้ก่อนหน้าออกจากอาร์เรย์ด้วยการกรอง

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");

การประกอบคลาสให้สมบูรณ์

นี่คือคลาสฉบับเต็มที่มีทั้งสามเมธอด และเป็นแบบทั่วไปเหนือแผนผังเหตุการณ์ใด ๆ ที่คุณจัดเตรียม

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");

การใช้งาน TypedEmitter

สร้างอินสแตนซ์ด้วยแผนผังเหตุการณ์ที่เป็นรูปธรรม จากนั้น on และ 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 { login: { userId: string } }
const bus = new TypedEmitter<Events>();
bus.on("login", p => console.log("hi", p.userId));
bus.emit("login", { userId: "u1" });

ตรวจจับข้อผิดพลาดด้านชนิดข้อมูลได้

คลาสนี้จะปฏิเสธเหตุการณ์ที่ไม่รู้จักและข้อมูลประกอบที่ไม่ถูกต้องตั้งแต่เวลาคอมไพล์ เช่นเดียวกับฟังก์ชันแบบแยกเดี่ยวก่อนหน้านี้

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");

หลายตัวจัดการต่อหนึ่งเหตุการณ์

เนื่องจากแต่ละเหตุการณ์จับคู่กับอาร์เรย์ คุณจึงลงทะเบียนตัวจัดการได้หลายตัว และทุกตัวจะทำงานเมื่อมีการส่งเหตุการณ์

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 });

รากฐานที่ต่อยอดได้

คลาสนี้เป็นรากฐานที่มั่นคง บทถัดไปเราจะปรับปรุงการอนุมานอาร์กิวเมนต์ของผู้รับฟัง และเพิ่มความหมายของ 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);

ตรวจสอบความเข้าใจอย่างรวดเร็ว: คลาสตัวส่งเหตุการณ์แบบทั่วไป

ทดสอบความเข้าใจเกี่ยวกับคลาสตัวส่งเหตุการณ์แบบทั่วไป

สรุป: คลาสตัวส่งเหตุการณ์แบบทั่วไป

คุณสร้าง TypedEmitter<E> พร้อมคลังเก็บ listeners ที่แมป และเมธอด on off และ 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)); }
}
const bus = new TypedEmitter<{ hi: { name: string } }>();
bus.on("hi", p => console.log(p.name));
bus.emit("hi", { name: "Ada" });

คำถามที่พบบ่อย

บทเรียน “คลาสตัวส่งเหตุการณ์แบบเจเนอริก” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “คลาสตัวส่งเหตุการณ์แบบเจเนอริก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “คลาสตัวส่งเหตุการณ์แบบเจเนอริก”

สร้างตัวส่งเหตุการณ์แบบมีชนิดข้อมูลที่นำกลับมาใช้ซ้ำได้ตั้งแต่ต้น คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “คลาสตัวส่งเหตุการณ์แบบเจเนอริก” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การกำหนดชนิดข้อมูลให้แผนผังเหตุการณ์
  2. emit และ on ที่ปลอดภัยด้านชนิดข้อมูล
  3. คลาสตัวส่งเหตุการณ์แบบเจเนอริก
  4. การอนุมานอาร์กิวเมนต์ของตัวรับฟัง
← กลับไปที่ TypeScript Academy