递归/层次变换

时间:2019-04-02 18:06:25

标签: typescript generics

我有一个函数,该函数采用具有相同类型的子数组的对象。现在,我想将孩子列表转换为由孩子ID索引的对象。

我想在Typescript中对此建模,并执行以下操作:

export const convertHierarchyToDiffFriendly = <T extends {id: string, children?: T[]}, U extends T & {children: {[id: string]: U}}>(x: T): U => (<U>{
    ...x,
    children: x.children ? 
        <U["children"]>x.children.reduce((z, y) => ({
            ...z,
            [y.id]: convertHierarchyToDiffFriendly(y)}),
            <{[id: string]: U}>{}) : {}
})

这可行,但是:

const convertedRhs = convertHierarchyToDiffFriendly(<IVendorStatusInput>rhs)
const c = convertedRhs.children["10"] // c's type is {}, not T & {children: {[id]: U}}

很明显,U不能正确导出,但是怎么可能呢?

1 个答案:

答案 0 :(得分:1)

我不认为您希望U是通用的。相反,您希望它是T类型的函数,并使用mappedconditional类型,如下所示:

type ConvertToDiffFriendly<T extends { id: string, children?: T[] }> = {
  [K in keyof T]: "children" extends K ? { [id: string]: ConvertToDiffFriendly<T> } : T[K]
};

因此,ConvertToDiffFriendly<T>T相同,只是其children属性(如果有)改变了类型。现在,您可以像下面这样键入函数:

export const convertHierarchyToDiffFriendly = <T extends { id: string, children?: T[] }>(
  x: T
): ConvertToDiffFriendly<T> => ({
  ...x,
  children: x.children ?
    x.children.reduce((z, y) => ({
      ...z,
      [y.id]: convertHierarchyToDiffFriendly(y)
    }),
      <{ [id: string]: ConvertToDiffFriendly<T> }>{}) : {}
} as ConvertToDiffFriendly<T>)

我没有检查实现的正确性,但是类型应该合理。

最后,让我们对其进行测试:

interface IVendorStatusThingy {
  id: string,
  children: IVendorStatusThingy[],
  limbs: number,
  withCheese: boolean
}
declare const rhs: IVendorStatusThingy;

const convertedRhs = convertHierarchyToDiffFriendly(rhs)    
const c = convertedRhs.children["10"] 
// c's type is ConvertToDiffFriendly<IVendorStatusThingy>

对我很好。希望能有所帮助;祝你好运!