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);
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With