我试图弄清楚如何有效地将强类型事件添加到我的项目中,但仍然遇到奇怪的问题。理想情况下,我希望能够做到这样的事情:
declare class EventEmitter<T> {
on<K extends keyof T>(event: K, fn: (...args: T[K]) => void, context?: any): void;
once<K extends keyof T>(event: K, fn: (...args: T[K]) => void, context?: any): void;
emit<K extends keyof T>(event: K, ...args: T[K]): boolean;
}
interface MyEvents {
'eventA': [string, number];
'eventB': [string, { prop: string, prop2: number }, (arg: string) => void];
}
class MyEmitter extends EventEmitter<MyEvents> {
// ...
}
const myEmitter = new MyEmitter();
myEmitter.on('eventA', (str, num) => {});
myEmitter.once('eventB', (str, obj, fn) => {});
myEmitter.emit('eventA', 'foo', 3);
第一个问题是,显然元组不是有效类型的休息参数,尽管只是引擎盖下的类型元素数组(我相信目前正在处理)。我想,如果我放弃键入emit
方法,并将我的事件映射到函数类型而不是元组,那就没问题了。这也可以提供关于参数的一些额外信息的好处。
declare class EventEmitter<T> {
on<K extends keyof T>(event: K, fn: T[K], context?: any): void;
once<K extends keyof T>(event: K, fn: T[K], context?: any): void;
}
interface MyEvents {
'eventA': (str: string, num: number) => void;
'eventB': (
str: string,
data: { prop: string, prop2: number },
fn: (arg: string) => void
) => void;
}
class MyEmitter extends EventEmitter<MyEvents> {
// ...
}
const myEmitter = new MyEmitter();
myEmitter.on('eventA', (str, num) => {});
myEmitter.once('eventB', (str, obj, fn) => {});
此时我很难过。 IntelliSense可以推断on
或once
的正确签名,但实际的参数类型仅针对其回调中具有最多参数的事件进行推断,这使得 no 感觉到我。我几天前开了an issue,但尚未得到答复。我不确定这实际上是一个错误,还是我忽略了什么。
与此同时,有没有有效的方法呢?我已经考虑过像这样向我的发射器类添加重载(这里EventEmitter
只是使用节点类型):
class MyEmitter extends EventEmitter {
on(event: 'eventA', fn: (str: string, num: number) => void);
on(event: 'eventB', fn: (
str: string,
data: { prop: string, prop2: number },
fn: (arg: string) => void
) => void);
}
但是,这要求我在我的班级中实际使用on
,如果我想要once
或emit
的类型,我必须复制所有的事件定义。有更好的解决方案吗?
答案 0 :(得分:1)
我commented报告您的问题;它似乎有点儿。显然,你可以通过在函数参数上注释类型来解决它。这很烦人,但它确实有效:
myEmitter.on('eventA', (str: string, num: number) => {}); // no errors
myEmitter.on('eventA', (str: string) => {}); // no error, [see FAQ](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters)
myEmitter.on('eventA', (str: number) => {}); // error as expected
myEmitter.on('eventA', (str: string, num: number, boo: boolean) => {}); // error as expected
你是对的,你不能使用元组类型作为休息参数。您可以通过将元组转换为数组来类型解决这个问题,但现在它会忘记订单:
type AsArray<T extends any[]> = (T[number])[]
declare class EventEmitter<T extends {[K in keyof T]: any[]}> {
on<K extends keyof T>(event: K, fn: (...args: AsArray<T[K]>) => void, context?: any): void;
once<K extends keyof T>(event: K, fn: (...args: AsArray<T[K]>) => void, context?: any): void;
emit<K extends keyof T>(event: K, ...args: AsArray<T[K]>): boolean;
}
myEmitter.emit('eventA', 2, 1); // oops, rest args are string|number
你可以通过找出合理的最大数量的函数参数(比如说4)并以这种方式声明它们来接近:
type TupleFunctionParams<T extends any[], R=void> = {
(a0: T[0], a1: T[1], a2: T[2], a3: T[3], a4: T[4], ...a5Plus: AsArray<T>): R
}
declare class EventEmitter<T extends {[K in keyof T]: any[]}> {
on<K extends keyof T>(event: K, fn: TupleFunctionParams<T[K]>, context?: any): void;
once<K extends keyof T>(event: K, fn: TupleFunctionParams<T[K]>, context?: any): void;
emit<K extends keyof T>(event: K, a0?: T[K][0], a1?: T[K][1], a2?: T[K][2], a3?: T[K][3], a4?: T[K][4], ...args: AsArray<T[K]>): boolean;
}
您在元组中指定的参数现在将以正确的顺序显示。但是,你仍然可以省略参数(see FAQ),你仍然可以指定额外的参数,这些参数将是元组中类型的联合:
myEmitter.emit('eventA', 1, 2); // error as expected
myEmitter.emit('eventA', 'one', 2); // works
myEmitter.emit('eventA', 'one'); // also works, all args are optional
myEmitter.emit('eventA', 'one', 2, 3) // ALSO works, because subsequent args are union
myEmitter.on('eventA', (str, num) => { }); // works
myEmitter.on('eventA', (str) => { }); // as above
myEmitter.on('eventA', (str, num, rando) => { }); // as above, rando is string | number
我能做的最好的事情就是在你的元组中加上一堆never
:
interface MyEvents {
'eventA': [string, number, never, never, never, never, never];
'eventB': [string, { prop: string, prop2: number }, (arg: string) => void, never, never, never, never];
}
现在至少额外的参数往往会有点打字:
myEmitter.emit('eventA', 'one', 2, 3) // Error on 3, should be undefined
myEmitter.on('eventA', (str, num, rando) => { }); // now rando is never
好的,这是我能做的最好的事情。希望能帮助到你。祝你好运!