我有一个提取嵌套 GraphQL 数据的函数:
const data = {
cats: { edges: [ {node: cat1}, {node: cat2} ] },
dogs: { edges: [ {node: dog1}, {node: dog2}, {node: dog3} ] },
}
const extracted = extract<{ cats: CatType, dogs: DogType }>(data);
// extracted = {
// cats: [cat1, cat2], // CatType[]
// dogs: [dog1, dog2, dog3], // DogType[]
// }
但是,我很难获得正确的类型签名。我想说的是“这个函数接受带有字符串键和类型值的接口的参数,并返回一个具有相同键的对象以及相应类型的数组值”。
换句话说,如果函数得到
{ cats: nestedCats }
,它应该返回 { cats: CatType[] }
(即 cats: T['cat'])
)。如果它得到 { dogs: nestedDogs }
,它应该返回 { dogs: DogType[] }
,鱼 { fishes: FishType[] }
,等等。如果它获取多种类型,它应该返回一个包含所有类型数组的对象。
现在我有一个签名:
export const extractNodesFromData = <T extends { [key: string]: any }> =>
但是,我有两个问题:
A)在我的函数内,当我迭代数据时,如何引用该数据的类型?例如:
Object.entries(data).map(([key, value]) => // How do I type value?
Object.entries(data).map((([key, value: T[key]]) => // Doesn't work
B)我无法指定函数的返回类型:我不知道如何将我提供的类型(
{ cat: CatType, ...}
)转换为相同的类型,但对于数组(即{ cat: CatType[], ...}
)...这就是该函数实际返回的内容。
完整可重现示例
这对实际问题没有任何帮助,但有些人可能会发现查看其他(不相关的)JS/TS 有帮助:
interface CatType {
id: string;
name: string;
catBreed: string;
}
interface DogType {
id: string;
name: string
dogBreed: string;
}
const extractNodes = <T>(data): T[] =>
data?.edges?.map(({ node }) => node);
const extractNodesFromData = <T extends { [key: string]: any }> (data: any) =>
Object.entries(data).reduce(
(extracted, [key, value: T[key]]) => ({
...extracted,
[key]: extractNodes<T[key]>(value)
}),
{}
)
这个答案的所有功劳都归功于@jcalz,他没有提交答案,但很友善地提供了一个完全工作的沙箱,它不仅回答了我的问题:它还为我修复了我的代码!我只是想学习如何钓鱼,而不是要求在它上面得到鱼......但我会接受:)
JCalz,请提交答案:我很乐意删除此答案并接受它(即使它是此答案的副本)。
如果您想访问泛型类型的属性,只需:
让你的泛型类型简单地扩展
object
{ [key: string]: any }
,这是我尝试过的在参数内部,使用语法
K in keyof T
作为解构后的参数的键。换句话说,不要执行以下任一操作:
(data: any) =>
(data: { [key : string[: any }) =>
改为:
(data: { [K in keyof T]: // ...无论你的值是什么
在我的具体情况下(提取
edge
和 node
),那就是:
(data: { [K in keyof T]: { edges: { node: T[K] }[] } })
上面的代码演示了我的问题的答案:要访问类型的属性,您do使用
T[K]
语法,这就是我正在尝试的,但因为它是与[K in keyof T]
相同的表达式的一部分您可以访问有效的 K
。