如何为GraphQL嵌套数据提取函数编写正确的TypeScript类型签名
解决GraphQL嵌套数据提取函数的TypeScript类型签名问题
需求回顾
我们需要实现一个函数,接收包含GraphQL风格嵌套结构的对象(每个属性值为带edges的连接对象),返回键名相同、值为对应节点类型数组的对象。例如输入{ cats: { edges: [...] } },输出{ cats: CatType[] }。
问题解决步骤
1. 定义GraphQL连接结构的通用类型
先统一约束GraphQL连接的结构,方便后续类型推导:
type GraphQLConnection<T> = { edges: Array<{ node: T }> };
2. 解决返回类型问题(问题B)
通过映射类型将输入对象的每个属性转换为对应节点的数组类型:
type ExtractConnectionNodes<T extends Record<string, GraphQLConnection<any>>> = { [K in keyof T]: T[K]['edges'][number]['node'][] };
这个类型会遍历输入对象T的所有键K,提取对应连接结构里的node类型,再包装成数组。
3. 修复单个连接的节点提取函数
给extractNodes添加正确的类型约束,确保它只接收符合GraphQL连接结构的参数:
const extractNodes = <T>(data: GraphQLConnection<T> | undefined): T[] => { return data?.edges?.map(({ node }) => node) || []; };
4. 解决遍历中的值类型问题(问题A)
给extractNodesFromData的参数指定正确的泛型约束,让TypeScript能自动推导Object.entries中value的类型,同时通过类型断言初始化reduce的累加器:
const extractNodesFromData = <T extends Record<string, GraphQLConnection<any>>>( data: T ): ExtractConnectionNodes<T> => { return Object.entries(data).reduce((extracted, [key, value]) => { return { ...extracted, [key]: extractNodes(value) }; }, {} as ExtractConnectionNodes<T>); };
这里data被约束为Record<string, GraphQLConnection<any>>,所以value会自动推导为对应键的GraphQLConnection<T>类型,无需手动指定T[key]。
完整可运行代码
interface CatType { id: string; name: string; catBreed: string; } interface DogType { id: string; name: string; dogBreed: string; } // 定义GraphQL连接结构的通用类型 type GraphQLConnection<T> = { edges: Array<{ node: T }> }; // 定义返回类型的映射类型 type ExtractConnectionNodes<T extends Record<string, GraphQLConnection<any>>> = { [K in keyof T]: T[K]['edges'][number]['node'][] }; // 提取单个连接中的节点数组 const extractNodes = <T>(data: GraphQLConnection<T> | undefined): T[] => { return data?.edges?.map(({ node }) => node) || []; }; // 批量提取多个连接中的节点数组 const extractNodesFromData = <T extends Record<string, GraphQLConnection<any>>>( data: T ): ExtractConnectionNodes<T> => { return Object.entries(data).reduce((extracted, [key, value]) => { return { ...extracted, [key]: extractNodes(value) }; }, {} as ExtractConnectionNodes<T>); }; // 示例使用 const cat1: CatType = { id: '1', name: 'Mimi', catBreed: 'Persian' }; const cat2: CatType = { id: '2', name: 'Luna', catBreed: 'Siamese' }; const dog1: DogType = { id: '3', name: 'Buddy', dogBreed: 'Labrador' }; const dog2: DogType = { id: '4', name: 'Max', dogBreed: 'German Shepherd' }; const dog3: DogType = { id: '5', name: 'Charlie', dogBreed: 'Poodle' }; const data = { cats: { edges: [{ node: cat1 }, { node: cat2 }] }, dogs: { edges: [{ node: dog1 }, { node: dog2 }, { node: dog3 }] }, }; const extracted = extractNodesFromData(data); // extracted类型为:{ cats: CatType[], dogs: DogType[] } console.log(extracted);
内容的提问来源于stack exchange,提问作者machineghost
相关产品推荐
相关产品推荐

