我正在尝试在TypeScript上实现简单的事件调度程序。
我的自定义类型的声明如下:
events.d.ts
:
type AppEvent = { [key: string]: any }
type AppEventListener<T> = <T extends AppEvent>(event: T) => void;
当我在此函数中传递任何对象时
public addListener<T extends AppEvent>(event: T, listener: AppEventListener<T>)
我收到以下错误:
Argument of type '(event: TestEvent) => void' is not assignable to parameter of type 'AppEventListener<typeof TestEvent>'.
Types of parameters 'event' and 'event' are incompatible.
Type 'T' is not assignable to type 'TestEvent'.
Property 'name' is missing in type 'AppEvent' but required in type 'TestEvent'.
因此,此类型AppEvent
不能具有任何其他属性。如何使其运作?任何帮助表示赞赏。
完整代码示例:
class EventDispatcher {
private listeners: Map<AppEvent, Array<AppEventListener<AppEvent>>>
constructor() {
this.listeners = new Map();
}
public dispatch<T extends AppEvent>(event: T): void {
const listeners = this.listeners.get(event.constructor);
if (listeners) {
listeners.forEach((callback) => {
callback(event);
});
}
}
public addListener<T extends AppEvent>(event: T, listener: AppEventListener<T>): void {
let listeners = this.listeners.get(event);
if (!listeners) {
listeners = [listener];
this.listeners.set(event, listeners);
} else {
listeners.push(listener);
}
}
}
class TestEvent {
public name: string = ''
}
const dispatcher = new EventDispatcher();
const listener = (event: TestEvent) => {
console.dir(event);
};
dispatcher.addListener(TestEvent, listener);
const event = new TestEvent('name');
dispatcher.dispatch(event);
答案 0 :(得分:2)
看起来您应该使AppEventLister
为泛型类型,以引用非泛型函数,如下所示:
type AppEventListener<T> = (event: T) => void;
您说的是“ AppEventListener<T>
将发生类型为T
的事件”。那就是你想要的,对吧?
您的原始定义是一个泛型类型,它引用一个泛型函数,具有两个同名的不同泛型参数,等效于
type AppEventListenerBad<T> = <U>(event: U) => void;
(重命名不会更改类型;它只会使类型参数名称阴影更清楚地说明了什么)。这意味着“ AppEventListener<T>
将完全忽略T
并接受任何事件键入呼叫者要指定的U
”,这不太可能是您的意思。这就是您的错误出处:(event: TestEvent) => void
无法分配给<U>(event: U) => void
。
执行此操作后,还会解决许多其他问题,但是它们可能会带来其他问题。一个主要的方面是standard library typings for Map
表示从K
类型的键到V
类型的值的非常基本的映射。它没有办法说K
的某些子类型的键映射到V
的以编程方式确定的子类型的值。在您的情况下,您可以为使用Map
的方式创建一个新界面:
interface AppEventListenerMap {
get<T>(x: T): Array<AppEventListener<T>> | undefined;
set<T>(x: T, val: Array<AppEventListener<T>>): void;
}
然后在EventDispatcher
中,将listeners
属性设置为类型AppEventListenerMap
的值:
class EventDispatcher {
private listeners: AppEventListenerMap;
constructor() {
this.listeners = new Map();
}
大多数情况下,这将使您的代码示例正常工作,但有时您似乎使用event
作为键,而其他时候似乎使用event.constructor
作为键。这种不一致似乎是错误的,您可能需要准确确定类型关系才能使其正常工作。
无论如何,希望这会有所帮助;祝你好运!