D3 (v4) and typescript: property 'x' does not exist in type 'HierarchyNode <IOrgChartNode>'
I am trying to use the correct ts types when using root d3.hierarchy
with tree layout like:
interface MyCustomGraphType {
id: string;
children?: MyCustomGraphType[]
}
let treeData: MyCustomGraphType = {/*...*/};
let root = d3.hierarchy(treeData);
let layout = d3.tree().size([500,500])(root);
let nodes = layout.descendants();
// BIND DATA (Node)
let node = this.nodesGroup.selectAll('g.node')
.data(nodes, d => d.data.person.email); // <--- compile-time error
// ^^^ Property 'data' does not exist on type '{} | HierarchyNode<MyCustomGraphType>'.
// ... later:
node.enter()
.append('circle')
.attr('transform', d => `translate(${d.x}, ${d.y})`); // <--- compile-time error
// ^^^ Property 'y' does not exist on type '{} | HierarchyNode<MyCustomGraphType>'.
Obviously, the first error is related to the fact that for some reason the type of the union '{} | HierarchyNode<MyCustomGraphType>'
is inferred in the function key
. The second error has to do with d3.tree
adding properties that were not previously defined there.
What's a clean way to approach this while maintaining type safety?
Thank!
PS I am using d3 version 4
source to share
There are a few things going on here that should be easy to resolve:
(1) To clarify, I am assuming that your actual data structure looks something like this:
interface MyCustomGraphType {
id: string;
person: {
email: string;
/*Other Properties*/
};
children?: MyCustomGraphType[];
}
This explains your access to the person.email
node privilege in the key function selection.data(...)
.
(2) D3 definitions make extensive use of type parameters. In some cases, type inference will serve them easily. In other cases, they cannot be easily understood.
- Use
d3.tree<MyCustomGraphType>().size([500,500])(root);
This will return the root point of the type tree layoutHierarchyPointNode<MyCustomGraphType>
. The implicationnodes
will now be an arrayHierarchyPointNode<MyCustomGraphType>[]
. -
select
,selectAll
,append
Anddata
from the module d3-selection have extensive JSDoc comments regarding the various generators overloaded signatures. They should be available as tooltips for tooltips or similar in a code editor (like VS Code).
(3) The reason why the key accessor is data
missing in method call errors is as follows: The key accessor is used to match old and new data records. The old data type is based on the previous expression selectAll(...)
. Given that the common types of the selected items and their "old" data type cannot be inferred from the string selector, they must be explicitly specified. Otherwise, the "old" data type defaults to {}
. This is why you see the data type of the union {} | HierarchyPointNode<MyCustomGraphType>
. It will be appreciated that the "old" data type of the selected items is synchronized between the actual selected items and the access key. The key function is to be able to handle edge cases if needed.
(4) Regarding the missing properties x
or y
, I can't seem to reproduce this issue. For me they are present since the data type is d
in
attr('transform', d => `translate(${d.x}, ${d.y})`)
correctly inferred as HierarchyPointNode<MyCustomGraphType>
.
Hope this explains it.
source to share