我一直把头撞在墙上,试图了解如何使函数成为类型安全的函数,但未能做到这一点。该函数应接收一个对象并返回一个数字。这是一个非常的简单示例(对于我的实际应用,接口更为复杂)。
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[]
,但我似乎无法正确地做到这一点并使函数纯粹是通用的。有什么想法吗?
答案 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">(...)
,但我只是在这里让您进行类型推断。
希望对您有用。如果它不适用于您的用例,请考虑编辑问题以包括更多详细信息。祝你好运!
答案 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) {
// ...
}