如何使用TypeScript和Apollo Client为GraphQL联合响应添加类型?
处理GraphQL联合类型的TypeScript类型推断方案
要让useQuery钩子的结果正确覆盖User和Error这两个联合片段的类型,有两种可行方案,优先推荐代码生成工具,避免手动编写类型时出错:
方法一:使用GraphQL Code Generator自动生成类型(推荐)
这个工具会根据你的GraphQL schema和查询语句,自动生成对应的TypeScript类型和类型安全的Apollo hooks,完美适配联合类型。
- 安装依赖
npm install --save-dev @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
- 配置生成规则
在项目根目录创建codegen.ts文件:
import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { schema: 'http://your-apollo-server-url/graphql', // 替换成你的Apollo Server地址,或者本地schema文件路径 documents: ['./src/**/*.graphql', './src/**/*.tsx'], // 搜索项目中所有包含GraphQL查询的文件 generates: { './src/generated/graphql.tsx': { plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'], config: { withHooks: true, // 生成useQuery等hooks withComponent: false, withHOC: false } } } }; export default config;
- 添加生成命令并执行
在package.json的scripts里添加:
"scripts": { "codegen": "graphql-codegen --config codegen.ts" }
然后运行:
npm run codegen
- 使用生成的类型安全hooks
生成完成后,你可以直接导入自动生成的useGetUserQuery钩子,它的返回数据会自动推断出user字段是User | Error | undefined类型:
import { useGetUserQuery } from './src/generated/graphql'; const UserComponent = () => { const { data, loading } = useGetUserQuery({ variables: { id: 'user-id-123' } }); if (loading) return <Text>Loading...</Text>; if (data?.user) { // 通过__typename做类型窄化,TypeScript会自动识别对应的类型 switch (data.user.__typename) { case 'User': return <Text>用户名:{data.user.username},邮箱:{data.user.email}</Text>; case 'Error': return <Text>错误:{data.user.message}</Text>; } } return <Text>无数据</Text>; };
方法二:手动定义类型并指定给useQuery
如果不想用代码生成工具,可以手动定义对应的TypeScript类型,然后在useQuery中指定泛型:
- 定义联合类型
// 定义User类型 type User = { __typename: 'User'; email: string; username: string; }; // 定义Error类型(避免和全局Error类型冲突,改名QueryError) type QueryError = { __typename: 'Error'; message: string; }; // 定义查询返回数据类型 type GetUserQueryData = { user: User | QueryError | undefined; };
- 在useQuery中指定泛型
import { useQuery } from '@apollo/client'; import { gql } from '@apollo/client'; const GET_USER = gql` query GetUser ($id: String!) { user(id: $id) { ... on User { email, username } ... on Error { message } } } `; const UserComponent = () => { const { data, loading } = useQuery<GetUserQueryData>(GET_USER, { variables: { id: 'user-id-123' } }); if (loading) return <Text>Loading...</Text>; if (data?.user) { switch (data.user.__typename) { case 'User': return <Text>用户名:{data.user.username},邮箱:{data.user.email}</Text>; case 'Error': return <Text>错误:{data.user.message}</Text>; } } return <Text>无数据</Text>; };
注意:不管用哪种方法,都要确保你的Apollo Server的GraphQL schema中正确定义了联合类型:
type User { email: String! username: String! } type Error { message: String! } union UserResult = User | Error type Query { user(id: String!): UserResult }
内容的提问来源于stack exchange,提问作者Mecks
相关产品推荐
相关产品推荐

