跳到主要内容

类型安全的事件

最近公司的开发用到TypeScript,自定义了一个类型安全的事件触发器,查了一下这样做有什么好处。

几乎是将下面这个自定义事件触发器搬过来用了。

https://jkchao.github.io/typescript-book-chinese/tips/typesafeEventEmitter.html

export interface Listener<T> {
(event: T): any;
}

export interface Disposable {
dispose(): any;
}

export class TypedEvent<T> {
private listeners: Listener<T>[] = [];
private listenersOncer: Listener<T>[] = [];

public on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);

return {
dispose: () => this.off(listener),
};
};

public once = (listener: Listener<T>): void => {
this.listenersOncer.push(listener);
};

public off = (listener: Listener<T>) => {
const callbackIndex = this.listeners.indexOf(listener);
if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
};

public emit = (event: T) => {
this.listeners.forEach(listener => listener(event));

this.listenersOncer.forEach(listener => listener(event));

this.listenersOncer = [];
};

public pipe = (te: TypedEvent<T>): Disposable => {
return this.on(e => te.emit(e));
};
}

Node.js里,是这样触发事件的:

import {EventEmitter} from 'events';

const eventEmitter = new EventEmitter();

eventEmitter.on('foo', data => {
console.log(data.foo);
});
eventEmitter.emit('foo', {foo: 'test'});

这样做有什么坏处呢?

首先,事件名完全是字符串,会有拼错的风险。

其次,事件的参数可以接受任何类型,也会有拼错的风险。

比如,误写成了eventEmitter.emit('fdo', {fdo: "test"});这样不显眼的错误很难排查,因为编译不出错。

所以如果利用类型安全,可以保证在编译里保证 触发的事件一定无误,而且 事件的参数 一定是某一个类型。

import {TypedEvent} from './typedEvent';
interface Foo {
test: string;
}

const fooEvent = new TypedEvent<Foo>();

fooEvent.on(data => {
console.log(data.test);
});

fooEvent.emit({test: 'foostring'});

如此,on的参数一定是(event: Foo): any函数,emit方法里必须要传入Foo类型的对象,否则编译不通过。