返回顶部
首页 > 资讯 > 精选 >TypeScript如何实现类型安全的EventEmitter
  • 515
分享到

TypeScript如何实现类型安全的EventEmitter

2023-07-05 09:07:03 515人浏览 八月长安
摘要

这篇文章主要介绍了typescript如何实现类型安全的EventEmitter的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇TypeScript如何实现类型安全的EventEmitter文章都会有所收获,下面

这篇文章主要介绍了typescript如何实现类型安全的EventEmitter的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇TypeScript如何实现类型安全的EventEmitter文章都会有所收获,下面我们一起来看看吧。

nodejs 的 EventEmitter 是一个发布订阅模块。

利用该类,我们可以实现事件的监听,被监听对象会在合适的时机触发事件,调用监听对象提供的方法,是模块间解耦的常用实现。

配合越来越流行的 TypeScript,我们可以通过安装 @types/node,我们能够进一步获得类型能力,减少低级错误的出现。但 EventEmitter 的类型实现并不出色,称不上是类型安全。

通常来说,不同事件对应的响应函数类型是不同的,但 @types/nodeEventEmiiter 类型没有提供高级类型,而是给一个异常宽松的值

class EventEmitter {  constructor(options?: EventEmitterOptions);  // 类型过于宽泛  on(eventName: string | symbol, listener: (...args: any[]) => void): this;  emit(eventName: string | symbol, ...args: any[]): boolean;  // ...其他}

可以看到,on 方法传入的事件名类型是 string | symbol,listener 则是随意任何类型的一个函数即可。emit 传入的参数也是 any[]

因为过于宽松的类型,如果事件名拼错了,TypeScript 并不会报错,当一个 eventEmitter 的事件类型变得非常多,我们就和裸写 javascript 没什么区别了。

自己动手,丰衣足食,我们不妨 自己实现一个类型安全的 EventEmitter

EventEmitter 实现

因为我其实是在前端用的 EventEmitter,所以写了一个 EventEmitter 简易 JavaScript 实现。

class EventEmitter {  eventMap = {};  // 添加对应事件的监听函数  on(eventName, listener) {    if (!this.eventMap[eventName]) {      this.eventMap[eventName] = [];    }    this.eventMap[eventName].push(listener);    return this;  }  // 触发事件  emit(eventName, ...args) {    const listeners = this.eventMap[eventName];    if (!listeners || listeners.length === 0) return false;    listeners.forEach((listener) => {      listener(...args);    });    return true;  }  // 取消对应事件的监听  off(eventName, listener) {    const listeners = this.eventMap[eventName];    if (listeners && listeners.length > 0) {      const index = listeners.indexOf(listener);      if (index > -1) {        listeners.splice(index, 1);      }    }    return this;  }}

如果你是 nodejs,继承 EventEmitter 然后改它的类型或许是更好的做法,或者可以 “基于组合而不是继承” 的方式实现一个。

类型安全的 EventEmitter

接着是将上面的代码改为 TypeScript。

我们希望的效果是:

const ee = new EventEmitter<{  update(newVal: string, prevVal: string): void;  destroy(): void;}>();const handler = (newVal: string, prevVal: string) => {  console.log(newVal, prevVal)}ee.on("update", handler);ee.emit('update', '前端西瓜哥上班前的精神状态', '前端西瓜哥上班后的精神状态')ee.off("update", handler);// 以下报错// 'number' is not assignable to parameter of type 'string'ee.emit('update', 1, 2)// (val: number) => void' is not assignable to parameter of type '() => voidee.on('destroy', (val: number) => {})

EventEmitter 支持接受一个对象结构的 interface 作为类型参数,指定不同的 key 对应的函数类型。

然后我们再调用 on、emit、off 时,如果事件名、函数参数不匹配,编译就不能通过

代码实现:

class EventEmitter<T extends Record<string | symbol, any>> {  private eventMap: Record<keyof T, Array<(...args: any[]) => void>> =    {} as any;  // 添加对应事件的监听函数  on<K extends keyof T>(eventName: K, listener: T[K]) {    if (!this.eventMap[eventName]) {      this.eventMap[eventName] = [];    }    this.eventMap[eventName].push(listener);    return this;  }  // 触发事件  emit<K extends keyof T>(eventName: K, ...args: Parameters<T[K]>) {    const listeners = this.eventMap[eventName];    if (!listeners || listeners.length === 0) return false;    listeners.forEach((listener) => {      listener(...args);    });    return true;  }  // 取消对应事件的监听  off<K extends keyof T>(eventName: K, listener: T[K]) {    const listeners = this.eventMap[eventName];    if (listeners && listeners.length > 0) {      const index = listeners.indexOf(listener);      if (index > -1) {        listeners.splice(index, 1);      }    }    return this;  }}

读者朋友可自行拷贝上面两段代码到 TypeScript Playground 测试一下。

简单讲解一下。

首先是开头的类型参数。

class EventEmitter<T extends Record<string | symbol, any>> {  //}

这里的 extends 作用是限定类型范围,防止提供一个不符合规则的类型参数。

Record 是 TypeScript 自带的高级类型,根据传入的 key 和 value 创建一个对象结构(后面说到的 T 就是它)。

Record<string | symbol, any>// 等价于{  [key: string | symbol]: any}

value 本来的类型应该是 (...args: any[]) => void,好限制为函数。但在不是非字面量类型直传的情况下无法通过类型检测,只好改成 any 了。(坑爹的 Index signature for type 'string' is missing 报错)

然后是 eventMap,它的实际内容是这样的:

eventMap = {  event1: [ handler1, handler2 ],  event2: [ handler3, handler4 ]}

所以 key 需要为传入对象类型参数的 key。

函数则不用指定特定类型,因为它是私有的,无法被类外部访问,没有做过多的类型推断,就宽松一些,设置为任何函数类型。

private eventMap: Record<keyof T, Array<(...args: any[]) => void>> =  {} as any;

这里我用了对象字面量,读者朋友也可以考虑用 Map 数据结构

然后是 on 方法,首先 eventName 必须为 T 的 key 的其中之一,因为要推断 K 这么个内部类型变量,所以我们要在 on 后面加上 <K extends keyof T>,listener 就是对应的 T[K]

on<K extends keyof T>(eventName: K, listener: T[K]): this

off 方法同理,不展开讲。

然后是 emit,第一个 eventName 用 keyof T 没问题,后面需要取出 handler 的参数,作为剩余参数。

emit<K extends keyof T>(eventName: K, ...args: Parameters<T[K]>): boolean

这里用了 TS 自带的 Parameters 高级类型,作用是取出函数的参数返回一个数组类型。

临时扩展自定义事件

如果要给一个已经固定了类型的实例,临时加一个事件,可以用 & 交叉类型扩展一下。

interface Events {  update(newVal: string, prevVal: string): void;  destroy(): void;}const ee = new EventEmitter<Events>();// 用 & 扩展const ee2 = ee as EventEmitter<  Events & {    customA(a: boolean): void;  }>;// 不报错ee2.emit('customA', true)// 或者(ee as EventEmitter<  Events & {    customA(a: boolean): void;  }>).emit('customA', true)

关于“TypeScript如何实现类型安全的EventEmitter”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“TypeScript如何实现类型安全的EventEmitter”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: TypeScript如何实现类型安全的EventEmitter

本文链接: https://www.lsjlt.com/news/350913.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • TypeScript如何实现类型安全的EventEmitter
    这篇文章主要介绍了TypeScript如何实现类型安全的EventEmitter的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇TypeScript如何实现类型安全的EventEmitter文章都会有所收获,下面...
    99+
    2023-07-05
  • TypeScript实现类型安全的EventEmitter
    目录正文EventEmitter 实现类型安全的 EventEmitter临时扩展自定义事件结尾正文 最近个人项目用 EventEmitter 模块越来越多了,因为类型不够安全,写...
    99+
    2023-03-06
    TS EventEmitter安全类型 TS EventEmitter
  • 如何使用TypeScript实现一个类型安全的EventBus
    这篇文章主要介绍“如何使用TypeScript实现一个类型安全的EventBus”,在日常操作中,相信很多人在如何使用TypeScript实现一个类型安全的EventBus问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对...
    99+
    2023-07-02
  • 使用TypeScript实现一个类型安全的EventBus示例详解
    目录前言准备工作目标思路具体实现全部代码后记前言 随着vue3的发布,TypeScript在国内越来越流行,学习TypeScript也随即变成了大势所趋。本文就通过实现一个类型安全的...
    99+
    2024-04-02
  • Typescript类型检查原理之Override如何实现
    这篇文章主要介绍了Typescript类型检查原理之Override如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Typescript类型检查原理之Override如何实现文章都会有所收获,下面我们一起...
    99+
    2023-06-05
  • VUE 嵌套路由与 TypeScript:提升类型安全性
    在 Vue.js 中使用嵌套路由是一种强大的技术,它允许您创建具有复杂导航结构的单页应用程序 (SPA)。 TypeScript 的加入进一步增强了这种功能,因为它提供了类型检查,有助于提高代码的健壮性和可维护性。 什么是嵌套路由? 嵌套...
    99+
    2024-04-02
  • TypeScript如何使用类型推导
    这篇文章给大家分享的是有关TypeScript如何使用类型推导的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。巧用类型推导TypeScript 能根据一些简单的规则推断(检查)变量...
    99+
    2024-04-02
  • TypeScript如何使用类型别名
    这篇文章主要介绍了TypeScript如何使用类型别名,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。类型别名类型别名会给一个类型起个新名字,...
    99+
    2024-04-02
  • TypeScript如何使用交叉类型
    这篇文章主要为大家展示了“TypeScript如何使用交叉类型”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“TypeScript如何使用交叉类型”这篇文章吧。交...
    99+
    2024-04-02
  • TypeScript如何使用类型约束
    这篇文章主要为大家展示了“TypeScript如何使用类型约束”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“TypeScript如何使用类型约束”这篇文章吧。巧...
    99+
    2024-04-02
  • 如何分析TypeScript枚举类型
    这篇文章给大家介绍如何分析TypeScript枚举类型,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1.概述所谓的枚举类型就是为一组数值赋予名字。enum类型在C++、Java语言中比较常见,TypeScript在Ja...
    99+
    2023-06-22
  • 如何使用TypeScript类型注解
    小编给大家分享一下如何使用TypeScript类型注解,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!类型注解TypeScript提供了很多数据类型,通过类型对变量进行限制,称之为类型注解,使用类型注解后,就不能够随意变更变...
    99+
    2023-06-29
  • 如何理解TypeScript枚举类型
    本篇内容主要讲解“如何理解TypeScript枚举类型”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解TypeScript枚举类型”吧!前言:TypeScript 在 ES 原有类型基础上...
    99+
    2023-06-25
  • TypeScript类型实现加减乘除详解
    目录引言分析DivideSmallerThanTupleSubtract最后加法乘法坑点总结引言 在网上看到这道题目:请用TS类型实现整除? type A = Divide<...
    99+
    2023-05-16
    TypeScript类型加减乘除 TypeScript 加减乘除
  • TypeScript类型怎么实现加减乘除
    这篇文章主要讲解了“TypeScript类型怎么实现加减乘除”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“TypeScript类型怎么实现加减乘除”吧!在网上看到这道题目:请用TS类型实现整...
    99+
    2023-07-06
  • TypeScript中的互斥类型实现方法示例
    目录前言前置知识对象中多属性同类型的定义never类型剔除联合类型中的属性将对象中的所有属性转为联合类型实现互斥类型实现代码测试用例用例拆解写在最后前言 有这样一个对象,它有两个属性...
    99+
    2024-04-02
  • TypeScript如何自定义数据类型
    这篇文章主要介绍“TypeScript如何自定义数据类型”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“TypeScript如何自定义数据类型”文章能帮助大家解决问题。TypeScript 类型系统和...
    99+
    2023-07-04
  • JavaScript如何实现发布订阅EventEmitter
    这篇文章主要为大家展示了“JavaScript如何实现发布订阅EventEmitter”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaScript如何实现发布订阅EventEmitter”...
    99+
    2023-06-27
  • 使用@ConfigurationProperties实现类型安全的配置过程
    目录@ConfigurationProperties实现类型安全的配置问题描述实践关于ConfigurationProperties注解的说明总结@ConfigurationProp...
    99+
    2023-02-13
    @ConfigurationProperties 类型安全配置 @ConfigurationProperties使用
  • node.js如何自定义实现一个EventEmitter
    目录前言 一、是什么 二、nodejs中EventEmitter使用方法 三、实现过程 前言 最近做了商品批发的需求,需要针对不同的商户选择对应的批发商品回显到原来的界面。由于该项...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作