Рекурсивное / иерархическое преобразование

#typescript #generics

#typescript #общие

Вопрос:

У меня есть функция, которая принимает объект с дочерним массивом того же типа. Теперь я хочу преобразовать список дочерних элементов в объект, индексируемый идентификатором дочерних элементов.

Я хотел бы смоделировать это в Typescript и выполнить следующее:

 export const convertHierarchyToDiffFriendly = <T extends {id: string, children?: T[]}, U extends T amp; {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 amp; {children: {[id]: U}}
  

Отчасти очевидно, что U не будет выведен правильно, но как это могло быть?

Ответ №1:

Я не думаю, что вы хотите U быть универсальным. Вместо этого вы хотите, чтобы это была функция T типа, использующая отображенные и условные типы, что-то вроде этого:

 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 свойство (если оно у него есть) изменено в type. Теперь вы можете ввести свою функцию следующим образом:

 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>
  

На мой взгляд, выглядит неплохо. Надеюсь, это поможет; удачи!

Комментарии:

1. Вы правы — это, должно быть, правильный путь. Спасибо!