React Query自定义查询构建器的TypeScript类型报错问题求助
解决React Query查询构建器中TypeScript类型推断失效问题
我明白你遇到的问题了——当给查询构建器的方法加上...config扩展后,TypeScript就无法正确推断useQuery返回的data类型了,这确实是React Query结合TypeScript时常见的类型陷阱之一。
问题根源
你当前使用的QueryObserverOptions是一个泛型类型,如果不指定泛型参数,它默认的data类型是unknown。当你展开...config时,TypeScript会认为这个config可能包含覆盖queryFn或者改变返回数据类型的配置,于是就把最终合并后的查询配置的data类型降级为unknown,导致后续访问events.data.locationEvents时报错。
而当你移除...config时,查询配置的queryFn明确返回Promise<LocationEvents>,React Query的useQuery能直接从queryFn的返回值推断出data的类型,所以一切正常。
解决方案
我们需要给QueryObserverOptions指定明确的泛型参数,让TypeScript知道查询的原始数据类型是LocationEvents,同时保留用户通过config(比如select选项)自定义返回数据类型的灵活性。
1. 给查询方法添加泛型支持
修改你的locationsQueries,让每个查询方法支持泛型,允许用户自定义最终返回的data类型(默认是LocationEvents):
import { QueryObserverOptions } from '@tanstack/react-query' import { get } from '../request/request' type LocationEvents = { locationEvents: Array<LocationEvent> } export const locationsQueries = { events: { // 泛型T允许用户通过select自定义返回类型,默认是LocationEvents all: <T = LocationEvents>( config: QueryObserverOptions<LocationEvents, unknown, T> = {} ) => ({ queryKey: [{ scope: 'locations', type: 'all' }] as const, queryFn: () => get('/locations/events').resolve<LocationEvents>(), ...config, }), byLocation: <T = LocationEvents>( locationId: string, config: QueryObserverOptions<LocationEvents, unknown, T> = {} ) => ({ queryKey: [ { scope: 'locations', type: 'byLocation', locationId }, ] as const, queryFn: () => get(`/locations/${locationId}/events`).resolve<LocationEvents>(), ...config, }), }, }
2. 验证效果
现在不管你是否传入config,TypeScript都能正确推断类型:
- 不传入config时:
const events = useQuery(locationsQueries.events.all()) // events.data 会被推断为 LocationEvents,访问locationEvents完全正常 - 传入带
select的config时:const events = useQuery(locationsQueries.events.all({ select: (data) => data.locationEvents })) // events.data 会被自动推断为 Array<LocationEvent>
额外说明
QueryObserverOptions的泛型参数顺序是:QueryObserverOptions<QueryFnData, Error, Data, QueryKey>,我们这里指定了QueryFnData为LocationEvents(queryFn返回的原始数据类型),Data为泛型T(最终返回给组件的数据类型)。- 确保你的
RequestBuilder.resolve方法的泛型是正确的,当前的async resolve<T extends unknown>(): Promise<T>写法没问题,但可以简化为async resolve<T>(): Promise<T>,效果是一样的。
内容的提问来源于stack exchange,提问作者Mugetsu
相关产品推荐
相关产品推荐

