RTK Query同端点不同响应的TypeScript类型定义问题
解决RTK Query中动态响应类型的TypeScript类型匹配问题
问题描述
我是TypeScript新手,在RTK Query中定义了一个查询端点,该端点会根据传入的filter参数返回不同结构的响应数组。但在组件中使用useGetIndicesQuery钩子时,TypeScript会因字段不存在的类型不匹配报错。相关代码如下:
export type RecentScore = { ndays: number | ''; score: number | '' }; export type TradeScore = { startValue: number | ''; score: number | ''; }; export interface IGetIndices { id: number; name: string; issuerAccountId: number; isDefault: boolean; startDate: Date; createdAt: Date; updatedAt: Date; } export interface IGetTradeVolume extends IGetIndices { scores: { M: TradeScore[] }; } export interface IGetRecent extends IGetIndices { scores: { R: RecentScore[] }; } getIndices: builder.query< IGetTradeVolume[] | IGetRecent[], { page: number; size: number; filter: string } >({ query: ({ page, size, filter }) => ({ url: `/club/indices?${ParamsHandler({ page, size, filter })}`, method: 'GET' }), providesTags: (result) => { return result ? [...result.map(({ id }) => ({ type: 'Club' as const, id })), 'Club'] : ['Club']; } })
解决方案
方案一:优化单个端点的类型定义(无需拆分)
核心是让TypeScript根据filter的具体值自动推断响应类型,通过条件类型关联参数与返回值:
- 定义filter与响应类型的映射关系
// 明确每个filter对应的响应类型 type FilterResponseMap = { 'trade-volume': IGetTradeVolume[]; 'recent': IGetRecent[]; }; // 提取合法的filter值类型 type ValidFilter = keyof FilterResponseMap;
- 修改查询端点的类型声明
通过泛型和条件类型实现参数与返回值的关联:
getIndices: <T extends ValidFilter>() => builder.query< FilterResponseMap[T], { page: number; size: number; filter: T } >({ query: ({ page, size, filter }) => ({ url: `/club/indices?${ParamsHandler({ page, size, filter })}`, method: 'GET' }), providesTags: (result) => { return result ? [...result.map(({ id }) => ({ type: 'Club' as const, id })), 'Club'] : ['Club']; } })
- 组件中使用时的自动类型推断
传入具体的filter值后,TypeScript会自动匹配对应响应类型:
// data会被推断为IGetTradeVolume[] const { data } = useGetIndicesQuery({ page: 1, size: 10, filter: 'trade-volume' }); // data会被推断为IGetRecent[] const { data } = useGetIndicesQuery({ page: 1, size: 10, filter: 'recent' });
方案二:拆分为独立端点(更直观)
如果filter对应的响应逻辑差异大,拆分端点能让代码更清晰,避免复杂类型逻辑:
- 拆分后的端点定义
// 交易成交量数据查询 getTradeVolumeIndices: builder.query<IGetTradeVolume[], { page: number; size: number }>({ query: ({ page, size }) => ({ url: `/club/indices?${ParamsHandler({ page, size, filter: 'trade-volume' })}`, method: 'GET' }), providesTags: (result) => { return result ? [...result.map(({ id }) => ({ type: 'Club' as const, id })), 'Club'] : ['Club']; } }), // 近期数据查询 getRecentIndices: builder.query<IGetRecent[], { page: number; size: number }>({ query: ({ page, size }) => ({ url: `/club/indices?${ParamsHandler({ page, size, filter: 'recent' })}`, method: 'GET' }), providesTags: (result) => { return result ? [...result.map(({ id }) => ({ type: 'Club' as const, id })), 'Club'] : ['Club']; } })
- 组件中直接调用对应钩子
const { data: tradeVolumeData } = useGetTradeVolumeIndicesQuery({ page: 1, size: 10 }); const { data: recentData } = useGetRecentIndicesQuery({ page: 1, size: 10 });
方案选择建议
- 若
filter对应的响应结构差异小、需灵活切换,优先用方案一,减少代码冗余; - 若响应结构差异大或后续会扩展更多filter类型,优先用方案二,代码可读性和可维护性更高。
内容的提问来源于stack exchange,提问作者Amir Rezvani
相关产品推荐
相关产品推荐

