【发布时间】:2020-10-27 10:46:15
【问题描述】:
我正在尝试通过EventEmitter 创建一个打字稿执行器。我的目标是使用addListener、removeListener 和emit 方法创建一个发射器,如果我传递一个无效事件或者即使我传递一个与事件签名不匹配的函数(args 和返回)。
为了实现这一点,我需要一个 Map 接口(一些带有函数列表的接口),一个例子可以在原生 GlobalEventHandlersEventMap 类型上看到:
lib.dom.d.ts
//...
interface GlobalEventHandlersEventMap {
"abort": UIEvent;
"animationcancel": AnimationEvent;
"animationend": AnimationEvent;
"animationiteration": AnimationEvent;
//...
到目前为止,我的前两种方法都是正确的:
import { EventEmitter } from "events";
// A simple mapping interface
interface SomeMap {
greeting: (message: string) => void;
}
// This generalization of function is necessary to track
// the list of arguments and result types.
interface GFunc<TArgs extends any[] = any[], TResult = any> {
(...args: TArgs): TResult;
}
// This is a advanced type to mask the EventEmitter class
interface AdvEventEmitter<T extends { [K in keyof T]: GFunc }> {
addListener<K extends keyof T>(event: K, listener: T[K]): void;
removeListener<K extends keyof T>(event: K, listener: T[K]): void;
}
const emitter: AdvEventEmitter<SomeMap> = new EventEmitter();
emitter.addListener('greeting', msg => {
console.log(msg);
});
在上面的代码中,AdvEventEmitter 接口能够对第一个参数进行约束:
emitter.addListener('something_else', () => {});
Msg: Argument of type '"something_else"' is not assignable to parameter of type '"greeting"'.
甚至在第二个参数中强制执行参数的类型和数量:
emitter.addListener('greeting', (m1, m2) => {
console.log(m1, m2);
});
Msg: Argument of type '(m1: any, m2: any) => void' is not assignable to parameter oftype '(message: string) => void'.
太棒了。
现在问题出在emit 方法。
我正在尝试这样的事情:
interface AdvEventEmitter<T extends { [K in keyof T]: GFunc }> {
// ...
emit<K extends keyof T, TArgs extends any[] = any[]>(event: K, ...args: TArgs): void;
}
arg event 已正确验证(如预期的那样),但 args 只是任何通用列表。我不知道如何将TArgs 约束与K 约束联系起来。
有没有办法强制执行此约束?
【问题讨论】:
标签: typescript generics constraints