Search code examples
typescriptd3.jseventstypes

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.


Solution

  • 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