2017-03-04 30 views
0

我正在构建一个小型事件处理类,并且遇到了打字问题,我无法完成工作。考虑以下几点:可以分别键入函数参数吗?

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}>(); 

这是很好的,我猜,但缺乏一定的打字稿-Y技巧。

回答

1

是否可以键入一个函数的参数分别

我不这么认为。至少我还没有能够。

但是,对于您的具体用例,我找到了一种方法来做到这一点。您可以通过参数签名获得类型检查分离方法(在您的案例中为triggerListener)的好处。在简化的方式:

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一个getter。 as any as A & (() => boolean)有一点奇怪的魔法,允许我们将返回的函数签名转换为您想要的参数,并根据需要返回值为boolean

不确定这是否理想,但至少它会掩盖班级中的丑陋,保持其界面优雅。

上述代码还允许省略泛型类型,只要您将参数传递给构造函数即可。所以这个作品完全一样:

const ed = new EventDispatcher((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 

而且它是免费获取自打字稿只是希望你声明的类型某处随时随地,无论是在构造函数或在泛型类型。(我没有测试过上述重复使用的代码,它省略了原有功能的一部分,但是我在成功使用该技术之后,可能会根据您的需要进行调整)。

+0

我认为这可能是一样好。它确实能够满足我的需求,尽管不幸的是更广泛的问题以“不”的方式回答。 –

相关问题