Búsqueda de sitios web

javascript - TypeScript: ¿cómo tipifico correctamente este nodo del árbol para que tenga una propiedad size o una propiedad children?


Tengo un tipo de nodo de árbol.

type Node = {
  name: string,
  size?: number,
  children?: Node[]
}

La idea aquí es que para los nodos hoja, deberían tener una propiedad size pero no una propiedad children. Para los nodos que tienen hijos, deberían tener una propiedad children pero no una propiedad size.

En este momento los marco a ambos como opcionales, pero en realidad, tienen una relación de uno u otro.

¿Cómo puedo ajustarlo para escribir correctamente un nodo así?

Respuesta #1

Para que esto funcione, quieres que Node sea un tipo unión donde cada miembro de la unión contenga explícitamente una propiedad y prohíba explícitamente la otra. Podría verse así:

interface BaseNode {
    name: string;
}
interface LeafNode extends BaseNode {
    size: number;
    children?: never
}
interface InternalNode extends BaseNode {
    size?: never;
    children: Node[]
}
type Node = LeafNode | InternalNode;

Aquí un Node es una unión de LeafNode y InternalNode, ambos heredan de un tipo común BaseNode con la propiedad común name (otras propiedades comunes irían allí). El tipo LeafNode requiere size y prohíbe children, mientras que InternalNode requiere children y prohíbe size.

Bueno, TypeScript en realidad no soporta directamente el concepto de prohibir una propiedad. En su lugar, puedes decir que una propiedad es opcional (así que se permite que esté ausente) y que su tipo es el tipo never imposible (así que nunca encontrarás un valor que se le permita tener). Eso es lo más cerca que podemos llegar.


Vamos a probarlo:

const node: Node = {
    name: "root",
    children: [
        { name: "tooMany", size: 1, children: [] }, // error
        { name: "tooFew" } // error
    ]
}

Se ve bien. El nodo "root" está bien porque tiene children pero no size, y el nodo "leaf" está bien porque tiene size pero no children. Por otro lado, el nodo "tooMany" produce un error de compilación porque tiene tanto size como children, y el nodo "tooFew" produce un error de compilación porque no tiene ni size ni children.

También tenga en cuenta que puede tratar Node como una unión discriminada siempre que su verificación sea para ver si la propiedad está definida o es undefined:

function processNode(node: Node): number[] {
    return node.children ?
        node.children.flatMap(processNode) : [node.size];
}

Enlace de Playground al código