我有一个树节点类型。
type Node = {
name: string,
size?: number,
children?: Node[]
}
这里的想法是,对于叶节点,它应该具有size
属性,但没有children
属性。对于具有子节点的节点,它应该具有children
属性,但没有size
属性。
现在我将它们都标记为可选,但实际上,它们具有非此即彼的关系。
如何调整它以正确键入这样的节点。
为了使它起作用,您希望Node
成为联合类型,其中联合的每个成员显式包含一个属性并显式禁止另一个属性。 它可能看起来像这样:
interface BaseNode {
name: string;
}
interface LeafNode extends BaseNode {
size: number;
children?: never
}
interface InternalNode extends BaseNode {
size?: never;
children: Node[]
}
type Node = LeafNode | InternalNode;
这里的Node
是LeafNode
和InternalNode
的并集,两者都继承自具有公共name
属性的公共BaseNode
类型(其他公共属性将在那里)。LeafNode
类型要求size
并禁止children
,而InternalNode
需要children
并禁止size
。
好吧,TypeScript 实际上并不直接支持禁止属性的概念。 相反,你可以说一个属性是可选的(所以它被允许不存在),并且它的类型是不可能的never
类型(所以你永远不会找到它允许具有的值)。 这是我们所能达到的尽可能接近的程度。
让我们测试一下:
const node: Node = {
name: "root",
children: [
{ name: "tooMany", size: 1, children: [] }, // error
{ name: "tooFew" } // error
]
}
看起来不错。"root"
节点没问题,因为它有children
但没有size
,"leaf"
节点没问题,因为它有size
但没有children
。 另一方面,"tooMany"
节点产生编译器错误,因为它同时具有size
和children
,而"tooFew"
节点产生编译器错误,因为它既没有size
也没有children
。
另请注意,您可以将Node
视为可区分的并集,只要您的检查是查看属性是否已定义或undefined
:
function processNode(node: Node): number[] {
return node.children ?
node.children.flatMap(processNode) : [node.size];
}
操场链接到代码