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

时间:2019-07-04 10:13:46

标签: typescript typescript-generics

我正在尝试编写一个函数,该函数采用一个像对象之类的通用树,该对象由像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对象,那么当我以期望的格式返回对象时,打字稿为什么会抱怨?

2 个答案:

答案 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;
}