Felipe Chernicharo
Felipe Chernicharo

Reputation: 4527

D3 event: Type definitions in version 6+

I have a group of widgets developed with d3 (version 5) and now I have to migrate them to version 7. In the migration guide https://observablehq.com/@d3/d3v6-migration-guide I've learned the new syntax for events:

// before
element.on('click', (d, i, e) => ...


// now
element.on('click', (event, d) => ...

Once I switched to the new syntax the program started running again. Yay!

The issue now is that I use typescript in this project! And Though I have installed the latest @types/...

  "dependencies": {
      "d3": "^7.1.1",
         ...

  "devDependencies": {
      "@types/d3": "^7.1.0",
         ...

...I keep having trouble with type definitions and Intellisense when it comes to events.

// 1. before (Worked fine)
element.on('click', (d: MyData, i: number, e: whatever[]) => ...


// 2. introducing types == Error back! ❌  
element.on('click', (event: PointerEvent, d: MyData) => ...


// 3. then, my (ugly) workaround:
element.on('click', (event: any , d: any) => ...

What's funny is that though I have @types/d3 v7.0.1 installed, the Typings system looks like it still holds reference to the old v5 format. Somehow Intellisense still thinks my first param is data and the second is a numeric index:

enter image description here

enter image description here

...But in real life what I get is { event: PointerEvent, d: MyData }, as shown in the picture below.

enter image description here

So, in a nutshell, feels like there's a problem with D3's types declaration as Intellisense thinks the Event's still under the v5 format while in real life it's not.

Do anyone know how to fix this typing thing? It's really annoying having to assign "any" to the whole thing inside my events.

Upvotes: 8

Views: 1969

Answers (2)

I solved this by adding a utility type of D3Event to my codebase that accepts 2 type args:

export type D3Event<T extends Event, E extends Element> = T & { currentTarget: E }

Then I can use it like this:

selection.on('mouseover', (event: D3Event<MouseEvent, SVGGElement>, d: SomeType) => { ... }

It's a little more verbose but it's much stricter than any

Upvotes: 3

altocumulus
altocumulus

Reputation: 21578

As you are well aware, it is usually best practice to avoid any like the Plague, which is the reason why you are trying to narrow it down to a particular type! In case of the event listener's type definition you are out of luck, though. Looking at the the .on method's definition you will notice that the event parameter is indeed typed as any allowing for the most generic event handling:

on(typenames: string, listener: (this: GElement, event: any, d: Datum) => void, options?: any): this;

For that reason, your second approach is going to fail leaving you with the third approach as the correct solution.

Upvotes: 4

Related Questions