d3.js - d3 (v4) diseño de árbol y typescript: La propiedad 'x' no existe en el tipo 'HierarchyNode'
Estoy intentando usar tipos TS adecuados mientras uso una raíz d3.hierarchy con un diseño de árbol, por ejemplo:
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>'.
Claramente, el primer error se debe a que, por alguna razón, el tipo de unión '{} | HierarchyNode<MyCustomGraphType>' se infiere en la función key. El segundo error se debe al hecho de que d3.tree añade propiedades que previamente no estaban definidas allí.
¿Cuál es una forma limpia de abordar esto manteniendo la seguridad de tipos?
¡Gracias!
P.D. Estoy usando la versión 4 de d3
Respuesta #1
Hay algunas cosas sucediendo aquí, que deberían poder resolverse fácilmente:
(1) Para aclarar, supongo que su estructura de datos real es algo más como esto:
interface MyCustomGraphType {
id: string;
person: {
email: string;
/*Other Properties*/
};
children?: MyCustomGraphType[];
}
Esto explicaría que estés accediendo a la propiedad person.email de un nodo en la función clave selection.data(...).
(2) Las definiciones de D3 hacen un uso extensivo de parámetros de tipo genéricos. En algunos casos, la inferencia de tipos los proporcionará fácilmente. En otros, no pueden inferirse fácilmente.
- Usa
d3.tree<MyCustomGraphType>().size([500,500])(root);Esto devolverá un punto raíz del diseño del árbol de tipoHierarchyPointNode<MyCustomGraphType>. Por implicación,nodesahora será un arrayHierarchyPointNode<MyCustomGraphType>[]. select,selectAll,appendydatadel módulo d3-selection tienen extensos comentarios JSDoc sobre los genéricos de las diversas firmas sobrecargadas. Deberían estar disponibles como sugerencias al pasar el mouse o similares en el editor de código (por ejemplo, VS Code).
(3) La razón por la que el accesor de clave en la llamada al método data genera un error es la siguiente: El accesor de clave se utiliza para emparejar entradas de datos antiguas y nuevas. El tipo de datos antiguo se basa en la instrucción selectAll(...) previa. Dado que los tipos genéricos de los elementos seleccionados y su tipo de dato "antiguo" no se pueden inferir a partir de un selector basado en cadena, deben establecerse explícitamente. De lo contrario, el tipo de datos "antiguo" se asigna por defecto a {}. Por eso se ve el tipo de unión de datos {} | HierarchyPointNode<MyCustomGraphType>. Se debe tener cuidado de que el tipo de datos "antiguo" de los elementos seleccionados esté sincronizado entre los elementos seleccionados reales y el accesor de clave. La función de clave debería tener una forma de manejar casos límite, si es necesario.
(4) En cuanto a las propiedades faltantes x o y, no parece que pueda replicar este problema. Para mí están presentes, ya que el tipo de datos de d en
attr('transform', d => `translate(${d.x}, ${d.y})`)
se infiere correctamente como HierarchyPointNode<MyCustomGraphType>.
Con suerte, esto explica.
