这是针对几种不同类型的节点类的设置,其中一些具有children
属性,每个属性都可以包含这些类的子集。最后,有一个类型别名(NodeABC
)将所有类型绑定在一起。
还有一个接口,用于定义可能是children
的所有可能类型的并集。
最后一部分是一个类型保护功能(hasChildren
)来检查使用这个统一界面的对象。
class NodeA {
children: NodeA[]
}
class NodeB {}
class NodeC {
children: NodeBC[]
}
type NodeAB = NodeA | NodeB
type NodeBC = NodeB | NodeC
type NodeABC = NodeAB | NodeBC
interface WithChildren {
children: NodeABC[]
}
const hasChildren = (item: NodeABC): item is WithChildren => {
return "children" in item
}
现在,当我创建一个包含所有节点类型的列表并尝试迭代它并访问子节点时:
const list = [new NodeA(), new NodeB(), new NodeC()] as NodeABC[]
list.forEach(item => {
if (!hasChildren(item)) {
return
}
// Type error here (no compatible call signatures)
item.children.forEach((child: NodeABC) => {})
})
这将不工作。它正确地看到item
不能NodeB
,但它children
将不具有类型保护的接口,而是NodeA
和NodeC
的联合children
的{{1}}财产。
const getChildren = (item: NodeABC): NodeABC[] => {
return hasChildren(item) ? item.children : []
}
list.forEach(item => {
getChildren(item).forEach(child => {})
})
我当然可以添加演员,但这需要一直发生。
我现在找到的解决方法是这个(没有强制转换):
Board operator+(const Board& b1, const Board& b2) {
Board ret;
for (int y = 0; y < N; y++) {
for (int x = 0; x < N; x++) {
ret.blk[y][x] = b1.blk[y][x] + b2.blk[y][x];
}
}
return ret;
}
哪个好,但我很想理解为什么第一个解决方案不起作用?类型保护显然不够“强大”足以说服类型系统的其余部分,这在某种程度上是有道理的,但在这种情况下,我想要的别名应该等于推断类型。我错过了什么?