我正在尝试使用具有树状布局的d3.hierarchy
根时使用正确的ts类型,例如:
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>'.
显然,第一个错误是因为无论出于何种原因,在'{} | HierarchyNode<MyCustomGraphType>'
函数中推断出了联合类型key
。第二个错误是由于d3.tree
添加了以前未在那里定义的属性。
在保持类型安全的同时,采用干净的方法是什么?
谢谢!
P.S。我正在使用d3版本4
答案 0 :(得分:3)
这里有一些事情,应该很容易解决:
(1)为了澄清,我假设您的实际数据结构更像是这样:
interface MyCustomGraphType {
id: string;
person: {
email: string;
/*Other Properties*/
};
children?: MyCustomGraphType[];
}
这可以解释您访问person.email
键功能中节点的selection.data(...)
属性。
(2)D3定义广泛使用泛型类型参数。在某些情况下,类型推断可以很容易地为它们服务。在其他情况下,不能轻易推断出它们。
d3.tree<MyCustomGraphType>().size([500,500])(root);
这将返回HierarchyPointNode<MyCustomGraphType>
类型的树布局根点。暗示nodes
现在将成为HierarchyPointNode<MyCustomGraphType>[]
数组。select
,selectAll
,append
和data
对各种重载签名的泛型有广泛的JSDoc评论。它们应该在代码编辑器中以鼠标悬停提示或类似方式提供(例如VS代码)。(3)data
方法中的密钥访问者调用错误的原因如下:密钥访问者用于匹配旧和 new 数据条目。旧数据类型基于前面的selectAll(...)
语句。鉴于无法从基于字符串的选择器推断所选元素的泛型类型及其“旧”数据类型,因此必须明确设置它们。否则,“旧”数据类型默认为{}
。这就是你看到联合数据类型{} | HierarchyPointNode<MyCustomGraphType>
的原因。必须注意所选元素的“旧”数据类型在实际所选元素和密钥访问者之间是同步的。如果需要,关键功能应该有办法处理边缘情况。
(4)对于缺失的属性x
或y
,我似乎无法复制此问题。对我来说,它们存在,作为<{p>中的d
的数据类型
attr('transform', d => `translate(${d.x}, ${d.y})`)
被正确推断为HierarchyPointNode<MyCustomGraphType>
。
希望这可以解释。