Typescript的复杂通用类型

时间:2019-11-07 18:50:24

标签: typescript

我一直把头撞在墙上,试图了解如何使函数成为类型安全的函数,但未能做到这一点。该函数应接收一个对象并返回一个数字。这是一个非常的简单示例(对于我的实际应用,接口更为复杂)。

interface Parent {
  id: number;
  children: Child[];
}

interface Child {
  text: string;
}

const parents: Parent[] = [
  {
    id: 1, 
    children: [
      {text: 'Child 1a'}, {text: 'Child 1b'}, 
      {text: 'Child 1c'}, {text: 'Child 1d'}, 
      {text: 'Child 1e'}
    ]
  },
  {
    id: 2, 
    children: [
      {text: 'Child 2a'}, {text: 'Child 2b'}, {text: 'Child 2c'}
    ]
  }  
];

function getMaxNumChildren<T>(data: T[], childKey: keyof T) {
  return data.reduce((max: number, parent: T) => {
    return max > parent[childKey].length ? max : parent[childKey].length;
  }, 0);
}

console.log(getMaxNumChildren<Parent>(parents, 'children')); // 5

因此,您可以想象,parent[childKey].length会引发错误,因为打字稿实际上并不知道T[keyof T]是一个数组。

除其他随机因素外,我尝试将其强制转换为any[],但我似乎无法正确地做到这一点并使函数纯粹是通用的。有什么想法吗?

3 个答案:

答案 0 :(得分:3)

我可以想象得到的最简单的方法是使函数K中的泛型,childKey的类型,并注释data是带有键的对象数组K和带有数字length属性的属性中,例如:

function getMaxNumChildren<K extends keyof any>(
  data: Array<Record<K, { length: number }>>,
  childKey: K
) {
  return data.reduce((max, parent) => {
    return max > parent[childKey].length ? max : parent[childKey].length;
  }, 0);
}

然后,编译器将能够验证parent[childkey]具有数字length并且没有错误。然后,您可以这样称呼它:

console.log(getMaxNumChildren(parents, 'children')); // 5

请注意,您不再调用getMaxNumChildren<Parent>(...),因为通用类型是键类型,而不是对象类型。您可以可以致电getMaxNumChildren<"children">(...),但我只是在这里让您进行类型推断。


希望对您有用。如果它不适用于您的用例,请考虑编辑问题以包括更多详细信息。祝你好运!

Link to code

答案 1 :(得分:0)

您太复杂了。只需使用父级数组

function getMaxNumChildren<T>(data: Parent[]T[], keyGetter: (obj: T) => Array<unknown>) {
  return data.reduce((max: number, parent: ParentT) => {
    return Math.max > (keyGetter(parent.children).length ?, max : parent.children.length;);
  }, 0);
}


更新的答案

比滥用类型系统更好地使用回调。

function getMaxNumChildren<T>(data: T[], keyGetter: (obj: T) => Array<unknown>) {
  return data.reduce((max: number, parent: T) => {
    return Math.max(keyGetter(parent).length, max);
  }, 0);
}

您可以这样使用它:

console.log(getMaxNumChildren<Parent>(parents, (p) => p.children));

答案 2 :(得分:0)

您需要让TypeScript知道两种通用类型,而不仅仅是一种。第一个是某个键,第二个是某个对象,其中该键的值是数组。

尝试这样的事情:

function getMaxNumChildren<TKey extends string, TObj extends { [key in TKey]: unknown[] }>(data: TObj[], childKey: TKey) {
    // ...
}