Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make data() key function to work with TypeScript?

I am trying to use a custom key function for .data() with .selectAll() selection in D3. But, making it TypeScript compatible is becoming such a pain.

The elements are not getting removed properly without the key function as D3 uses the index to check for updates by default.

So, here is the JavaScript code snippet, working all fine as expected:

const circles = svg
        .select("g")
        .selectAll("circle")
        .data(data, d => d.val);

Trying the same with TypeScript,

interface IData {
  val: number;
}

Attempt 1

const circles = svg
        .select("g")
        .selectAll("circle")
        .data<IData>(data, (d) => d.val);
Object d is of type 'unknown'.  TS2571

    35 |         .select("g")
    36 |         .selectAll("circle")
  > 37 |         .data<IData>(data, (d) => d.val);

Attempt 2

const circles = svg
        .select("g")
        .selectAll<SVGSVGElement, IData>("circle")
        .data<IData>(data, (d) => d.val);
No overload matches this call.
  Overload 1 of 3, '(data: IData[], key?: ValueFn<SVGSVGElement | Element | EnterElement | Document | Window | null, IData, string> | undefined): Selection<...>', gave the following error.
    Argument of type '(this: SVGSVGElement | Element | EnterElement | Document | Window | null, d: IData) => number' is not assignable to parameter of type 'ValueFn<SVGSVGElement | Element | EnterElement | Document | Window | null, IData, string>'.
      Type 'number' is not assignable to type 'string'.
  Overload 2 of 3, '(data: ValueFn<BaseType, unknown, IData[]>, key?: ValueFn<SVGSVGElement | Element | EnterElement | Document | Window | null, IData, string> | undefined): Selection<...>', gave the following error.
    Argument of type 'IData[]' is not assignable to parameter of type 'ValueFn<BaseType, unknown, IData[]>'.
      Type 'IData[]' provides no match for the signature '(this: BaseType, datum: unknown, index: number, groups: BaseType[] | ArrayLike<BaseType>): IData[]'.  TS2769

And, it is all working fine without the key function for data:

const circles = svg
        .select("g")
        .selectAll<SVGSVGElement, IData>("circle")
        .data<IData>(data);
like image 682
Himanshu Avatar asked Apr 05 '20 09:04

Himanshu


1 Answers

As can be seen from the docs the key function needs to return a string (emphasis mine):

A key function may be specified to control which datum is assigned to which element, replacing the default join-by-index, by computing a string identifier for each datum and element.

This can also be seen by looking at the type definitions for selection.data():

data<NewDatum>(data: NewDatum[], key?: ValueFn<GElement | PElement, Datum | NewDatum, string>): Selection<GElement, NewDatum, PElement, PDatum>;

Herein, the key function is defined as

key?: ValueFn<GElement | PElement, Datum | NewDatum, string>

with the ValueFn type being

export type ValueFn<T extends BaseType, Datum, Result> = (this: T, datum: Datum, index: number, groups: T[] | ArrayLike<T>) => Result;

Looking at the above two definitions it is easy to see that the Result type parameter is set to string for the key function definition, which is the reason for your compiler error.

Returning a string from your key function will solve this issue:

.data(data, d => "" + d.val);
like image 171
altocumulus Avatar answered Nov 08 '22 21:11

altocumulus