microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.67k stars 12.44k forks source link

Overriding addEventListener to augment event objects #9604

Closed d180cf closed 8 years ago

d180cf commented 8 years ago

Ultimately, what I want to do is to describe the following trick:

const element = document.createElement("div");

element.addEventListener("click", event => {
  event.foo = 123;
});

As you see, my <div> element intercepts all "click" events and adds a new property to them, so whoever attaches a "click" listener later will see this new property.

A practical example would be a custom UI element that represents, for instance, a chess board, and adds row and col properties to every mouse event, as the consumers of the UI element shouldn't need to convert the plain x and y coordinates to the cell coordinates.

My attempt to describe this failed:

interface MyMouseEvent {
  foo?: number;
}

interface MyElement extends HTMLElement {
  addEventListener(type: "click", listener: (ev: MyMouseEvent) => any, capture?: boolean): void;
}

tsc complains that the HTMLElement interface is incorrectly extended:

TS2430  Interface 'MyElement' incorrectly extends interface 'HTMLElement'.
  Types of property 'addEventListener' are incompatible.
    Type '(type: "click", listener: (ev: MyMouseEvent) => any, useCapture?: boolean) => void' is not assignable to type '{ (type: "MSContentZoom", listener: (ev: UIEvent) => any, useCapture?: boolean): void; (type: "MS...'.
      Type '(type: "click", listener: (ev: MyMouseEvent) => any, useCapture?: boolean) => void' provides no match for the signature '(type: string, listener: EventListener | EventListenerObject, useCapture?: boolean): void'
kitsonk commented 8 years ago

When you have overrides that is also an overload, you have to provide at least one overload that matches the overridden function. This is fairly clearly implied by the error:

interface MyElement extends HTMLElement {
  addEventListener(type: "click", listener: (ev: MyMouseEvent) => any, capture?: boolean): void;
  addEventListener(type: string, listener: EventListener | EventListenerObject, useCapture?: boolean): void;
}
d180cf commented 8 years ago

This works nicely, indeed. Thanks!