如何使用Typescript创建EventHandler类?

时间:2018-12-09 11:51:26

标签: typescript generics

我正在尝试将我的项目从javascript迁移到打字稿,并且在处理事件的类移植方面遇到问题。

为了避免重复描述添加/删除事件侦听器的选项,我们使用如下包装器:

constructor() {
  this.windowResizeHandler = new MyEventHandler(
    target: window,
    event: 'resize',
    handler: e => this.handleResize_(e),
    options: {passive: true, capturing: true},
  );
} 

connectedCallback() {
  this.windowResizeHandler.add();
}

disconnectedCallback() {
  this.windowResizeHandler.remove();
}

现在,在不丢失有关事件键入的信息的情况下,我不知道如何在打字稿中编写此内容。例如:

document.createElement('button').addEventListener('click', e => {
  // Here e is MouseEvent.
});

但是如果我像这样写包装器:

interface EventHandlerParams {
  readonly target: EventTarget;
  readonly event: Event;
  readonly listener: (e: Event) => void;
  readonly params: AddEventListenerOptions;
}

export class EventHandler {
  public constructor(params: EventHandlerParams) {}
}

然后我松散输入:

new MyEventHandler(
  target: document.createElement('button'),
  event: 'click',
  handler: e => { /* Here e is just Event not MouseEvent */ },
  options: {passive: true, capturing: true},
);

我在这里是否可以使用lib.dom.d.ts中的事件类型?

2 个答案:

答案 0 :(得分:1)

lib.dom.ts中有一个类型,其中包含所有事件名称和事件参数类型之间的映射。它称为WindowEventMap

因此我们可以例如编写以下内容:

interface EventHandlerParams<T extends keyof WindowEventMap> {
    readonly target: EventTarget;
    readonly event: T;
    readonly options: AddEventListenerOptions;
    readonly listener: (e: WindowEventMap[T]) => void
}

export class EventHandler<T extends keyof WindowEventMap> {
    public constructor(params: EventHandlerParams<T>) { }
}


new EventHandler({
    target: document.createElement('button'),
    event: 'click',
    options: { passive: true },
    listener: e => { e.x /* e is MouseEvent */ }
});

EventHandlerParams现在是通用的,它将捕获事件名称作为类型参数T。我们还使EventHandler通用,并且T取决于传递给它的婴儿车。有了T(它将包含事件的字符串文字类型),我们可以从WindowEventMap访问事件的实际参数类型并将其用于我们的侦听器签名中。

注意,我认为3.0之前的版本可能不会将侦听器的参数推断为正确的类型(它们可能会推断为any)。如果您遇到此问题,请告诉我,我可以提供3.0之前的版本。

答案 1 :(得分:0)

@ titian-cernicova-dragomir我想要这样的东西(不起作用):

interface Mapping {
  [Window]: WindowEventMap;
  [HTMLElement]: HTMLElementEventMap;
}

interface EventHandlerParams<TTarget extends keyof Mapping,
                             TEventName extends keyof Mapping[TTarget],
                             TEvent extends Mapping[TTarget][TEventName]> {
  readonly event: TEventName;
  readonly listener: (event: TEvent) => void;
  readonly params?: AddEventListenerOptions;
  readonly target: TTarget;
}


export class EventHandler<TTarget extends keyof Mapping,
                          TEventName extends keyof Mapping[TTarget],
                          TEvent extends Mapping[TTarget][TEventName]> {
  public constructor(params: EventHandlerParams<TTarget, TEventName, TEvent>) {}
}

但是不能使用它,因为类型不能是接口属性,并且没有任何其他选项可以为TTarget提供约束。