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