I'm working through the example Force Dragging I and have been able to get this to work with plain JavaScript. However, I have a compilation error when trying to use TypeScript.
The problem code (with extraneous parts removed) is:
import * as d3 from "d3";
interface INode {
id: string;
group: number;
}
interface ILink {
source: string;
target: string;
value: number;
}
interface IGraph {
nodes: INode[];
links: ILink[];
}
var svg = d3.select("svg");
d3.json("data/miserables.json", function (error, graph: IGraph) {
var node = svg.append("g")
.selectAll("circle")
.data(graph.nodes) // Commenting this out, error goes away
.enter().append("circle")
.call(d3.drag() // Error here
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
});
The .call(d3.drag()
line is giving rise to the following tsc error:
App.tsx(31,15): error TS2345: Argument of type 'DragBehavior' is not assignable to parameter of type '(selection: Selection, ...args: any[]) => void'. Types of parameters 'selection' and 'selection' are incompatible. Type 'Selection' is not assignable to type 'Selection'. Type 'BaseType' is not assignable to type 'Element'. Type 'null' is not assignable to type 'Element'.
Similar to d3 v4 drag and drop with TypeScript, there is no error if I change the code to reselect the circles:
var nodes =
svg.append("g")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle");
var circle = svg.selectAll("circle");
circle.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
However, when not using the types this is not necessary.
I'm trying to work out how to work with TypeScript here in order to define variables with the appropriate types, or whatever is required to get it compiling.
A little preamble:
The TypeScript definitions for the D3 modules make extensive use of generics in interfaces and methods, most importantly the generics are used to control the type of element(s) D3 operates on, as well as the type of the data that are bound to those elements.
To the core of your issue:
(1) When you create your drag behavior, ensure that the generics correspond to the element type and bound data type corresponding to the element to be dragged. In the above case, this should imply d3.drag<SVGCircleElement, INode>()
(2) Ensure the drag event handlers (dragstarted
, dragged
and dragended
have a matching call signature, e.g. function dragged(this: SVGCircleElement, d: INode) {...}
(You will only need to specify the this
-context type, if you intend to access it in the callback.)
(3) Ensure the selection to which you apply the drag behavior has the corresponding type. The Selection
interface has four generics, of which the first two again correspond to the type of the selected element and the type of its bound datum. Selection
methods like select
, selectAll
, data
and append
all have generics corresponding to "things" they may impact. In the case of data
, the generic may be inferred by TS based on the data that are passed into the call. In the case of the other mentioned methods, you may have to explicitly spell out, what it is that you are "selecting" or "appending". For the above, you could consider:
//...
.data<INode>(graph.nodes) // Commenting this out, error goes away
.enter()
.append<SVGCircleElement>("circle")
//...
The above changes should ensure your selection and drag behavior are aligned in a type-safe manner. For more details you can refer to the comments in the definitions:
DragBehavior Definition or the related definition tests here. Selection Definition. Since all these TS definitions have extensive JSDoc comments, a good IDE will have hover tooltips/peeks allowing you to see them as you write. Look out for the part of comments referring to generics. Hope this helps.
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