函数参数可以单独输入吗?

时间:2017-03-04 16:27:30

标签: generics typescript

我正在构建一个小型的事件处理类,我遇到了一个我无法完成工作的打字问题。请考虑以下事项:

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技巧。

1 个答案:

答案 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只是希望你在构造函数或泛型类型中声明类型某处任何地方

(我之前没有测试过您的改编代码,它省略了原始功能的一部分,但我在成功之前就已经使用过这种技术,您可以根据自己的需要调整它。)