通用类型 return 不接受包含相同定义字段的对象

Generic type return does not accept object containing the same defined fields

我正在尝试编写一个函数,它接受一个像对象的通用树,该对象由像 children 这样的节点组成,而 optionaly 又可以有自己的 children,第二个参数是一个函数predicate,将当前访问的节点传递给过滤函数

我不太关心 treechildren 除外)和 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 对象,为什么当我 return 一个预期格式的对象时打字稿会抱怨?

T 扩展了 TreeLike,但 TreeLike 没有扩展 T。代码尝试 return TreeLike 而不是 T。

newTree 的类型应该是 T,但它是 '{ children: NodeLike[]; }'.

最简单的修复

const newTree = {
    ...t, 
    children: t.children.filter(predicate)
};

我可能误解了你想要什么,但对我来说,更通用的方法可能是用功能术语来描述你的树抽象,而不是每个节点上的已知 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;
}