在扩展EventEmitter的TypeScript类中声明事件

ale*_*ung 13 node.js typescript

我有一个EventEmitter可以发出事件的类扩展hello.如何on使用特定的事件名称和侦听器签名声明该方法?

class MyClass extends events.EventEmitter {

  emitHello(name: string): void {
    this.emit('hello', name);
  }

  // compile error on below line
  on(event: 'hello', listener: (name: string) => void): this;
}
Run Code Online (Sandbox Code Playgroud)

use*_*569 31

扩展@SergeyK 的答案,这样您就可以在不重复事件类型的情况下对两者emiton函数进行类型检查和完成。

  1. 为每个事件类型定义事件侦听器签名:
interface MyClassEvents {
  'add': (el: string, wasNew: boolean) => void;
  'delete': (changedCount: number) => void;
}
Run Code Online (Sandbox Code Playgroud)
  1. 声明为 构造类型的接口MyClass,基于 EventListeners( MyClassEvents) 函数签名:
declare interface MyClass {
  on<U extends keyof MyClassEvents>(
    event: U, listener: MyClassEvents[U]
  ): this;

  emit<U extends keyof MyClassEvents>(
    event: U, ...args: Parameters<MyClassEvents[U]>
  ): boolean;
}
Run Code Online (Sandbox Code Playgroud)
  1. 简单地定义你的类扩展EventEmitter
class MyClass extends EventEmitter {
  constructor() {
    super();
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您将获得类型检查onemit功能:

在此处输入图片说明

在此处输入图片说明

不幸的是,您只能对这两个函数进行补全和类型检查(除非您在 MyClass 接口中定义更多函数)。

要获得更通用的解决方案,您可以使用这个包注意:它不会增加运行时开销。

import { TypedEmitter } from 'tiny-typed-emitter';

interface MyClassEvents {
  'add': (el: string, wasNew: boolean) => void;
  'delete': (changedCount: number) => void;
}

class MyClass extends TypedEmitter<MyClassEvents> {
  constructor() {
    super();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 那个包绝对是太棒了。*到目前为止*我迄今为止见过的最用户友好的解决方案。谢谢你! (4认同)

Ser*_*eyK 30

如果你想查看特定事件名称的类型,据我所知你只能使用这样的界面:

declare interface MyClass {
    on(event: 'hello', listener: (name: string) => void): this;
    on(event: string, listener: Function): this;
}

class MyClass extends events.EventEmitter {
    emitHello(name: string): void {
        this.emit('hello', name);
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这样的声明,当你使用类型的变量declare时,在输入export函数时会有一个类型的智能感知建议

更新1

第二种方式,我认为更有用的是使用declare:

declare interface MyClass {
    on(event: 'hello', listener: (name: string) => void): this;
    on(event: string, listener: Function): this;
}

class MyClass extends events.EventEmitter {
    emitHello(name: string): void {
        this.emit('hello', name);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 只需使** Update 1 **成为答案即可,因为另一种方法更成问题。 (2认同)

小智 6

这是我能够弄清楚的。用泛型覆盖默认函数!

interface IEmissions {
  connect: () => void
  test: (property: string) => void
}

class MyClass extends events.EventEmitter {
  private _untypedOn = this.on
  private _untypedEmit = this.emit
  public on = <K extends keyof IEmissions>(event: K, listener: IEmissions[K]): this => this._untypedOn(event, listener)
  public emit = <K extends keyof IEmissions>(event: K, ...args: Parameters<IEmissions[K]>): boolean => this._untypedEmit(event, ...args)

  this.emit('test', 'Testing') // This will be typed for you!
}

// Example:
const inst = new MyClass()
inst.on('test', info => console.log(info)) // This will be typed!
Run Code Online (Sandbox Code Playgroud)


Mor*_*lod 5

您可以为此使用类型化事件发射器包

例如:

import { EventEmitter } from 'tsee';

const events = new EventEmitter<{
    foo: (a: number, b: string) => void,
}>();

// foo's arguments is fully type checked
events.emit('foo', 123, 'hello world');
Run Code Online (Sandbox Code Playgroud)

这个包还提供了接口和一些实用程序。


小智 5

我真的很喜欢@Binier的答案,尤其是tiny-typed-emitter提供的通用解决方案。作为替代方案,我编写了这个纯打字稿版本:

type EmittedEvents = Record<string | symbol, (...args: any) => any>;

export declare interface TypedEventEmitter<Events extends EmittedEvents> {
  on<E extends keyof Events>(
    event: E, listener: Events[E]
  ): this;

  emit<E extends keyof Events>(
    event: E, ...args: Parameters<Events[E]>
  ): boolean;
}

export class TypedEventEmitter<Events extends EmittedEvents> extends EventEmitter {}
Run Code Online (Sandbox Code Playgroud)

它的使用方式类似:

type MessageSocketEvents = {
  'message': (json: object) => void;
  'close': () => void;
};

export class MessageSocket extends TypedEventEmitter<MessageSocketEvents> {
  ...
}
Run Code Online (Sandbox Code Playgroud)