91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用TypeScript實現一個類型安全的EventBus

發布時間:2022-09-23 15:01:57 來源:億速云 閱讀:157 作者:iii 欄目:開發技術

這篇文章主要介紹“如何使用TypeScript實現一個類型安全的EventBus”,在日常操作中,相信很多人在如何使用TypeScript實現一個類型安全的EventBus問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何使用TypeScript實現一個類型安全的EventBus”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

準備工作

生成一個TypeScript的基礎架子:

// 創建目錄
mkdir ts-event-bus && cd ts-event-bus
// 初始化工程
yarn init -y
// 安裝typescript
yarn add typescript -D
// 生成typescript配置文件
npx tsc --init

這樣一來我們就搭建好了一個TypeScript的基礎架子,為了方便我們后續的測試,我們需要下載ts-node,它可以讓我們在不編譯TypeScript代碼的情況下運行TypeScript

yarn add ts-node -D

目標

  • 基礎功能完備,包括注冊,發布,取消訂閱三個核心功能。

  • 類型安全,能約束我們輸入的參數,并且有代碼提示。

思路

每一個Event都可以注冊多個處理函數,我們用一個Set來保存這些處理函數,再用一個Map來保存Event到對應Set的映射,如圖所示:

如何使用TypeScript實現一個類型安全的EventBus

具體實現

// 定義泛型函數類型
type Handler<T = any> = (val: T) => void;
class EventBus<Events extends Record<string, any>> {
  /** 保存 key => set 映射 */
  private map: Map<string, Set<Handler>> = new Map();
  on<EventName extends keyof Events>(
    name: EventName,
    handler: Handler<Events[EventName]>
  ) {
    let set: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!set) {
      set = new Set();
      this.map.set(name as string, set);
    }
    set.add(handler);
  }
}

這里我們分成邏輯和類型兩方面來講

邏輯方面,我們初始化了一個空的Map,然后當調用on 用來注冊事件的時候,先去根據EventName來找有沒有對應的Set,沒有就創建一個,并且把事件添加到Set中,這一部分的代碼相當簡單,實現起來也沒什么難度。

類型方面,我們將EventBus 定義為一個泛型類,并約束泛型為 Events extends Record<string, any>,這樣就約束了傳入的泛型參數必須是一個對象類型,例如:

type Events = {
    foo : number;
    bar : string;
}

我們可以通過這個類型來獲取key對應value的類型

// number;
type ValueTypeOfFoo = Events['foo']

進而可以獲取foo事件對應的handler函數的類型,即:

// (val:number) => void;
type HandlerOfFoo = Handler<Events['foo']>

我們又將on方法設置為泛型函數,同時約束EventName extends keyof Events,這樣一來Events[EventName] 就是對應值的類型,Handler<Events[EventName]>就是處理函數的類型。通過這樣的方式我們實現了一個類型安全的on方法。

接著我們編寫一段代碼測試一下

可以看到,我們在vscode中編寫代碼的時候,編輯器能給我們代碼提示了。

如何使用TypeScript實現一個類型安全的EventBus

我們鍵入handler函數,編輯器也會提醒我們val是一個string類型。

如何使用TypeScript實現一個類型安全的EventBus

當我們傳的參數不合法的時候,TypeScript也會給我們警告

如何使用TypeScript實現一個類型安全的EventBus

如何使用TypeScript實現一個類型安全的EventBus

接下來我們依葫蘆畫瓢實現emit函數。

class EventBus<Events extends Record<string, any>> {
 ... others code
  /** 觸發事件 */
  emit<EventName extends keyof Events>(
    name: EventName,
    value: Events[EventName]
  ) {
    const set: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!set) return;
    const copied = [...set];
    copied.forEach((fn) => fn(value));
  }
}

先找到EventName對應的Set,如果有就取出并依次執行。這里的邏輯也相當簡單,我們編寫代碼測試一下

const bus = new EventBus<{
  foo: string;
  bar: number;
}>();

bus.on("foo", (val) => {
  console.log(val);
});

// 輸出 hello
bus.emit("foo", "hello");

我們在終端運行npx ts-node ./index.ts,輸出hello,說明我們的程序已經生效。

接下來我們實現取消訂閱的功能。

{
  ...
  off<EventName extends keyof Events>(
    name?: EventName,
    handler?: Handler<Events[EventName]>
  ): void {
    // 什么都不傳,則清除所有事件
    if (!name) {
      this.map.clear();
      return;
    }

    // 只傳名字,則清除同名事件
    if (!handler) {
      this.map.delete(name as string);
      return;
    }

    // name 和 handler 都傳了,則清除指定handler
    const handlers: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!handlers) {
      return;
    }
    handlers.delete(handler);
  }
}

取消訂閱我們這樣設計,它傳入0至2個參數,什么都不傳代表清除所有事件,只傳一個參數代表清除同名事件,傳兩個參數代表只清除該事件指定的處理函數,所以它的兩個參數都是可選的,實現的邏輯也非常簡單,我們這里不多贅述。

我們編寫一段測試代碼看下效果

const bus = new EventBus<{
  foo: string;
  bar: number;
}>();

// 測試傳2個參數的情況
const handlerFoo1 = (val: string) => {
  console.log("2個參數 handlerFoo1 => ", val);
};
bus.on("foo", handlerFoo1);
bus.emit("foo", "hello");
// 打印 2個參數 handlerFoo1 => hello
bus.off("foo", handlerFoo1);
bus.emit("foo", "hello");
// 什么都沒打印

// 測試傳1個參數的情況
const handlerFoo2 = (val: string) => {
  console.log("1個參數 handlerFoo2 => ", val);
};
const handlerFoo3 = (val: string) => {
  console.log("1個參數 handlerFoo3 => ", val);
};
bus.on("foo", handlerFoo2);
bus.on("foo", handlerFoo3);

bus.emit("foo", "hello");
// 打印 1個參數 handlerFoo2 => hello
// 打印 1個參數 handlerFoo3 => hello
bus.off("foo");
bus.emit("foo", "hello");
// 什么都沒輸出

// 測試傳0個參數的情況
const handlerFoo4 = (val: string) => {
  console.log("0個參數 handlerFoo4 => ", val);
};
const handlerBar1 = (val: number) => {
  console.log("0個參數 handlerBar1 => ", val);
};
bus.on("foo", handlerFoo4);
bus.on("bar", handlerBar1);

bus.emit("foo", "hello");
bus.emit("bar", 123);
// 打印 1個參數 handlerFoo4 => hello
// 打印 1個參數 handlerBar1 => 123
bus.off();
bus.emit("foo", "hello");
bus.emit("bar", 123);
// 什么都沒輸出

從測試結果來看,我們的off方法功能也沒問題,這樣就完成了我們的EventBus

此外,我們還可以給我們的方法加上注釋,這樣在我們鼠標移到api上方和我們輸入參數的時候,編輯器就會有提示。

  /**
   * 訂閱事件
   * @param name 事件名
   * @param handler 事件處理函數
   */
  on<EventName extends keyof Events>(
    name: EventName,
    handler: Handler<Events[EventName]>
  ) {
    let set: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!set) {
      set = new Set();
      this.map.set(name as string, set);
    }
    set.add(handler);
  }

如何使用TypeScript實現一個類型安全的EventBus

如何使用TypeScript實現一個類型安全的EventBus

可以看到,編輯器給我們提供了很好的提示,極大方便了我們的編碼。

我們還可以用函數重載來改進我們的off方法,以獲得更友好的提示

{
  /**
   *  清除所有事件
   */
  off(): void;
  /**
   * 清除同名事件
   * @param name 事件名
   */
  off<EventName extends keyof Events>(name: EventName): void;
  /**
   * 清除指定事件
   * @param name 事件名
   * @param handler 事件處理函數
   */
  off<EventName extends keyof Events>(
    name: EventName,
    handler: Handler<Events[EventName]>
  ): void;
  off<EventName extends keyof Events>(
    name?: EventName,
    handler?: Handler<Events[EventName]>
  ): void {
    // 什么都不傳,則清除所有事件
    if (!name) {
      this.map.clear();
      return;
    }

    // 只傳名字,則清除同名事件
    if (!handler) {
      this.map.delete(name as string);
      return;
    }

    // name 和 handler 都傳了,則清除指定handler
    const handlers: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!handlers) {
      return;
    }
    handlers.delete(handler);
  }
}

改造前的提示:

如何使用TypeScript實現一個類型安全的EventBus

改造后的提示:

如何使用TypeScript實現一個類型安全的EventBus

至此,我們就完成了一個功能完備,類型安全的EventBus了。

全部代碼

type Handler<T = any> = (val: T) => void;

class EventBus<Events extends Record<string, any>> {
  private map: Map<string, Set<Handler>> = new Map();

  /**
   * 訂閱事件
   * @param name 事件名
   * @param handler 事件處理函數
   */
  on<EventName extends keyof Events>(
    name: EventName,
    handler: Handler<Events[EventName]>
  ) {
    let set: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!set) {
      set = new Set();
      this.map.set(name as string, set);
    }
    set.add(handler);
  }

  /**
   * 觸發事件
   * @param name 事件名
   * @param handler 事件處理函數
   */
  emit<EventName extends keyof Events>(
    name: EventName,
    value: Events[EventName]
  ) {
    const set: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!set) return;
    const copied = [...set];
    copied.forEach((fn) => fn(value));
  }
  /**
   *  清除所有事件
   */
  off(): void;
  /**
   * 清除同名事件
   * @param name 事件名
   */
  off<EventName extends keyof Events>(name: EventName): void;
  /**
   * 清除指定事件
   * @param name 事件名
   * @param handler 處理函數
   */
  off<EventName extends keyof Events>(
    name: EventName,
    handler: Handler<Events[EventName]>
  ): void;

  off<EventName extends keyof Events>(
    name?: EventName,
    handler?: Handler<Events[EventName]>
  ): void {
    // 什么都不傳,則清除所有事件
    if (!name) {
      this.map.clear();
      return;
    }

    // 只傳名字,則清除同名事件
    if (!handler) {
      this.map.delete(name as string);
      return;
    }

    // name 和 handler 都傳了,則清除指定handler
    const handlers: Set<Handler<Events[EventName]>> | undefined = this.map.get(
      name as string
    );
    if (!handlers) {
      return;
    }
    handlers.delete(handler);
  }
}

const bus = new EventBus<{
  foo: string;
  bar: number;
}>();

// 測試傳2個參數的情況
const handlerFoo1 = (val: string) => {
  console.log("2個參數 handlerFoo1 => ", val);
};
bus.on("foo", handlerFoo1);
bus.emit("foo", "hello");
// 打印 2個參數 handlerFoo1 => hello
bus.off("foo", handlerFoo1);
bus.emit("foo", "hello");
// 什么都沒打印

// 測試傳1個參數的情況
const handlerFoo2 = (val: string) => {
  console.log("1個參數 handlerFoo2 => ", val);
};
const handlerFoo3 = (val: string) => {
  console.log("1個參數 handlerFoo3 => ", val);
};
bus.on("foo", handlerFoo2);
bus.on("foo", handlerFoo3);

bus.emit("foo", "hello");
// 打印 1個參數 handlerFoo2 => hello
// 打印 1個參數 handlerFoo3 => hello
bus.off("foo");
bus.emit("foo", "hello");
// 什么都沒輸出

// 測試傳0個參數的情況
const handlerFoo4 = (val: string) => {
  console.log("0個參數 handlerFoo4 => ", val);
};
const handlerBar1 = (val: number) => {
  console.log("0個參數 handlerBar1 => ", val);
};
bus.on("foo", handlerFoo4);
bus.on("bar", handlerBar1);

bus.emit("foo", "hello");
bus.emit("bar", 123);
// 打印 1個參數 handlerFoo4 => hello
// 打印 1個參數 handlerBar1 => 123
bus.off();
bus.emit("foo", "hello");
bus.emit("bar", 123);
// 什么都沒輸出

到此,關于“如何使用TypeScript實現一個類型安全的EventBus”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁津县| 滨州市| 神木县| 九龙城区| 武山县| 浦北县| 隆化县| 太保市| 云梦县| 平谷区| 高青县| 郸城县| 新绛县| 双城市| 莲花县| 东安县| 古蔺县| 余庆县| 色达县| 东兴市| 连州市| 宁乡县| 邵东县| 龙井市| 三门县| 永修县| 尤溪县| 平邑县| 罗定市| 井冈山市| 新余市| 会宁县| 镇坪县| 新野县| 武乡县| 绵竹市| 永康市| 小金县| 丰台区| 丹寨县| 且末县|