在Apollo TypeScript中为非空字段启用默认解析器的问题
问题描述
使用@graphql-codegen/cli结合Apollo构建GraphQL API,解析器基于TypeScript编写。遇到的问题是:非空字段的子解析器无法触发,因为TypeScript要求主解析器返回所有非空字段的值,导致子解析器完全没机会执行。
当前GraphQL定义:
type Post { id: ID! body: String! } input PostsRequest { start: Int end: Int cursor: Int } type PostsResponse { posts: [Post!]! totalCount: Int! } extend type Query { posts(request: PostsRequest!): PostsResponse! }
解析器代码:
export const PostsResolver: Resolvers<{ query: { value: number } }> = { Query: { posts: (_, { request: { start, end } }, context = { query: { value: 4 } }) => { const query = constructQuery(start ?? 0, end ?? 5); context.query = query; return { totalCount: -1, posts: [{ id: "-1", body: "you should never see this!" }], }; }, }, PostsResponse: { posts: (_1, _2, context) => { const results = executeQuery(context.query); return results.map((result) => ({ id: result.id, body: "you should never see this!" })); }, totalCount: (_1, _2, context) => executeCount(context.query), }, Post: { body: (post) => getPostBody(post.id), }, };
现在所有Post的body都返回占位文本"you should never see this!",而非getPostBody()的结果。
解决方案
1. 核心问题:提前返回子字段导致解析器跳过
Apollo的解析逻辑是:如果父解析器已经返回了某个字段的具体值,就不会再调用该字段对应的子解析器。你在PostsResponse.posts和Query.posts的返回值中都手动设置了body字段,导致Post.body解析器完全没机会执行。
解决方法:只返回Post的标识字段(id),去掉手动设置的body
修改后的解析器代码:
export const PostsResolver: Resolvers<{ query: { value: number } }> = { Query: { posts: (_, { request: { start, end } }, context = { query: { value: 4 } }) => { const query = constructQuery(start ?? 0, end ?? 5); context.query = query; return { totalCount: -1, posts: [{ id: "-1" }], // 仅返回id }; }, }, PostsResponse: { posts: (_1, _2, context) => { const results = executeQuery(context.query); return results.map((result) => ({ id: result.id })); // 仅返回id }, totalCount: (_1, _2, context) => executeCount(context.query), }, Post: { body: (post) => getPostBody(post.id), }, };
2. 处理TypeScript非空字段报错
修改后TypeScript会报错,因为Post类型要求body为非空字符串,但你返回的对象只有id。可以通过以下两种方式解决:
方式一:使用类型断言
在返回对象时添加类型断言,告诉TypeScript这是合法的Post类型:
return results.map((result) => ({ id: result.id }) as Post);
方式二:调整GraphQL Codegen配置(更优雅)
在codegen.ts中配置自定义映射类型,允许解析器返回仅包含必要字段的对象:
import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { // 其他基础配置(schema、documents等) generates: { './src/generated/resolvers-types.ts': { plugins: ['typescript', 'typescript-resolvers'], config: { // 配置自定义映射,让Post解析器的父对象可以是仅含id的类型 mappers: { Post: '../types#PostResolverParent', }, }, }, }, }; export default config;
然后在src/types.ts中定义映射类型:
export type PostResolverParent = { id: string };
重新生成类型后,TypeScript就会允许解析器返回仅包含id的对象,同时不会破坏类型安全。
内容的提问来源于stack exchange,提问作者AMWJ
相关产品推荐
相关产品推荐

