我正在构建一个小型的事件处理类,我遇到了一个我无法完成工作的打字问题。请考虑以下事项:
export interface IEventListener<A> {
(...A): boolean | void;
}
export default class EventDispatcher<A> {
private listeners: IEventListener<A>[] = [];
constructor(...listeners: IEventListener<A>[]) {
listeners.forEach((listener) => {
this.addListener(listener);
});
}
public addListener(listener: IEventListener<A>): void {
if (this.listeners.indexOf(listener) === -1) {
this.listeners.push(listener);
}
}
public removeListener(listener: IEventListener<A>): void {
const index = this.listeners.indexOf(listener);
if (index !== -1) {
this.listeners.splice(index, 1);
}
}
public triggerListeners(...A): boolean {
const args = arguments;
return this.listeners.every((listener) => {
return listener(args) === false;
});
}
}
(你不得不原谅那些伪造的语法)
这里,泛型类型A
代表传递给调度程序的侦听器和调度程序的triggerListeners
函数中使用的函数的参数的签名。
我真正希望能够做的是,使用强类型参数接口声明一个新的EventDispatcher
,如下所示:
const ed = new EventDispatcher<(name: string, street: string)>();
或者,更理想的是,使用推理:
const ed = new EventDispatcher((name: string, street: string) => {
console.log(name, street);
});
这样:
ed.triggerListeners("Sandy", "100th"); // no error
ed.triggerListeners("Sandy", 100); // error
是否可以单独输入函数的参数并将它们作为泛型类型传递?
目前,我已将其设置为侦听器只能接受单一类型的对象:
export interface IEventListener<T> {
(T): boolean | void;
}
const ed = new EventDispatcher<{name: string, street: string}>();
这很好,我猜,但缺少某种TypeScript-y技巧。
答案 0 :(得分:1)
是否可以单独输入函数的参数
我不这么认为。至少我没能。
但是,对于您的具体用例,我找到了一种方法。您可以使用参数签名检查分离方法(在您的情况下为else:
)的类型。以简化的方式:
triggerListener
代码的作用是提供一个getter,它返回export default class EventDispatcher<A extends Function> {
private listeners: A[] = [];
constructor(listener?: A) {
if (listener) {
this.addListener(listener);
}
}
public addListener(listener: A): void {
if (this.listeners.indexOf(listener) === -1) {
this.listeners.push(listener);
}
}
public removeListener(listener: A): void {
const index = this.listeners.indexOf(listener);
if (index !== -1) {
this.listeners.splice(index, 1);
}
}
public get triggerListeners() {
return this._triggerListeners as any as A & (() => boolean);
}
private _triggerListeners(...args: any[]): boolean {
return this.listeners.every((listener) => {
return listener.apply(null, args) === false;
});
}
}
const ed = new EventDispatcher<(name: string, street: string) => void>();
ed.addListener((name: string, street: string) => console.log(`${name}, ${street}`));
ed.triggerListeners("Sandy", 100); // Not ok: "Argument of type '100' is not assignable to parameter of type 'string'."
ed.triggerListeners("Sandy", "100"); // Ok
不同的特定参数签名。那里有一些奇怪的魔法_triggerListeners
允许我们将返回的函数签名输入到你想要的参数中,再加上你想要的返回值为as any as A & (() => boolean)
。
不确定它是否理想,但至少它会隐藏你班级内部的丑陋,保持其界面优雅。
只要您将参数传递给构造函数,上面的代码也允许省略泛型类型。所以这完全相同:
boolean
它是免费的,因为TypeScript只是希望你在构造函数或泛型类型中声明类型某处,任何地方。
(我之前没有测试过您的改编代码,它省略了原始功能的一部分,但我在成功之前就已经使用过这种技术,您可以根据自己的需要调整它。)