我正在尝试编写一个函数,该函数采用一个像对象之类的通用树,该对象由像children
这样的节点组成,而该结点又可以 optionaly 具有自己的children
,第二个参数是具有当前访问节点的函数predicate
,将其传递给过滤器函数。
我不太关心tree
(除了children
)和node
之类的对象内部的额外属性。因此,我选择使用泛型来实现此功能。下面是我的代码的简化版本。
interface NodeLike {
children?: NodeLike[];
}
interface TreeLike {
children: NodeLike[];
}
export function filterChildrenFromTree<T extends TreeLike, N extends NodeLike>(
t: T,
predicate: (n: N) => boolean
): T {
const newTree = {
children: t.children.filter(predicate)
};
return newTree;
}
不幸的是,打字稿在return
行下给了我以下错误:
Type '{ children: NodeLike[]; }' is not assignable to type 'T'.
如果在这种情况下声明T
扩展了TreeLike对象,那么当我以期望的格式返回对象时,打字稿为什么会抱怨?
答案 0 :(得分:2)
T扩展了TreeLike,但是TreeLike不扩展T。代码尝试返回TreeLike而不是T。
newTree的类型应为T,但应为'{children:NodeLike []; }'。
最简单的解决方法
const newTree = {
...t,
children: t.children.filter(predicate)
};
答案 1 :(得分:0)
我可能误解了您想要什么,但是对我来说,更通用的方法可能是用功能术语而不是每个节点上的已知children
属性来描述树的抽象。您可以定义以下函数类型,表示类型为T[]|undefined
的子代的可选性质,但是在这里我选择了一个空数组来表示相同的情况,因为它简化了逻辑:
type ChildSelector<T> = (item: T) => T[];
type Pred<T> = (item: T, index: number) => boolean;
type TreeAssembler<T> = (children: T[]) => T;
现在,您可以编写过滤功能,该功能对可过滤树的类型没有限制
export function filterChildrenFromTree<T>(
t: T,
childSelector: ChildSelector<T>,
predicate: Pred<T>,
treeAssembler: TreeAssembler<T>
): T {
const children = childSelector(t);
const newTree = treeAssembler(children.filter(predicate));
return newTree;
}