Apollo应用构建:TypeScript中GraphQL/Apollo类型问题求助
嘿,我太懂你现在的困扰了——在TypeScript环境里用多个Apollo高阶组件(HOC)组合数据时,类型推断总是乱套,总怀疑是不是自己的实现思路出了问题对吧?其实这个问题挺常见的,核心在于Apollo的HOC在处理类型传递时,需要更明确的类型声明,不然TypeScript没法正确合并多个HOC注入的Props。
我给你几个针对性的解决方案,你可以一步步试:
给每个HOC显式指定类型参数
别依赖TypeScript的自动推断,给每个withQuery或withMutation明确声明数据结构和变量类型。比如:// 先定义查询返回的数据结构和变量类型 interface ViewerData { viewer: { id: string; name: string }; } interface ViewerVars { userId: string } const withViewer = withQuery<ViewerData, ViewerVars>(VIEWER_QUERY, { variables: { userId: "123" } });这样HOC注入的Props(比如
data.viewer、data.loading)就会有精确的类型,不会和其他HOC的Props冲突。避免重复的Props键名
多个HOC默认都会注入loading、error这类通用Props,直接合并肯定会有类型冲突。解决办法有两个:要么给查询结果加别名,要么在HOC里重命名注入的Props:// 方案1:给查询结果加别名 const VIEWER_QUERY = gql` query ViewerQuery($userId: String!) { viewer: getUser(id: $userId) { id name } } `; // 方案2:在HOC的options里重命名Props const withViewer = withQuery(VIEWER_QUERY, { props: ({ data }) => ({ viewer: data.viewer, viewerLoading: data.loading, // 把通用的loading重命名为viewerLoading viewerError: data.error }) });这样每个HOC注入的Props都是唯一的,交叉类型合并时就不会有问题了。
改用Hooks替代HOC(更推荐)
现在Apollo官方更推荐用Hooks(useQuery、useMutation)来获取数据,TypeScript对Hooks的类型支持要比HOC好得多,而且不需要处理复杂的Props传递。比如把类组件改成函数组件:const Navigation: React.FC<Props> = ({ theme }) => { const { data: viewerData, loading: viewerLoading } = useQuery<ViewerData, ViewerVars>(VIEWER_QUERY, { variables: { userId: "123" } }); const { data: pageData, loading: pageLoading } = useQuery<PageData, PageVars>(PAGE_QUERY); if (viewerLoading || pageLoading) return <Spinner />; if (!viewerData?.viewer || !pageData?.currentPage) return <NotFound />; return ( <nav theme={theme}> {/* 直接用viewerData.viewer和pageData.currentPage渲染内容 */} </nav> ); };这种方式完全不需要手动合并Props,类型推断直接生效,代码也更简洁直观。
用compose时确保类型正确传递
如果你坚持用HOC,一定要用Apollo提供的compose工具(或者react-compose),它会正确合并多个HOC的类型。比如:import { compose } from '@apollo/client'; const NavigationContainer = compose( withViewer, withPage )(Navigation);这样TypeScript会自动把
withViewer和withPage注入的Props合并到组件的Props类型里,你甚至不需要手动写Props & ViewerProps & PageProps(不过手动写也没问题,只要类型不冲突)。
最后再提醒一句:检查你的ViewerProps和PageProps是否和Props有重复的键,如果有,要么重命名,要么用Omit类型排除重复项(比如Omit<ViewerProps, 'theme'>)。
内容的提问来源于stack exchange,提问作者Innominate

