useQuery包装器QueryKey类型不匹配问题排查
问题:自定义React Query钩子的类型错误
我在仪表盘项目中封装了useFilteredRequest钩子,用来统一从上下文获取重复参数,避免每个查询重复编写相同的参数逻辑。但当前代码遇到了类型错误,具体出现在构造queryKey的部分。
当前代码
import { useState, useEffect, useRef, useMemo } from 'react'; import { useQuery, QueryKey, QueryFunction, UseQueryOptions } from '@tanstack/react-query'; // 假设上下文钩子已实现 const usePageFilters = () => ({ category: '', classification: '', selectedBrands: [] }); const useFilterContext = () => ({ filterRequestParams: {} }); const useDateFilterContext = () => ({ dataFinalStr: '', dataInicialStr: '' }); interface IUseLazyQuery { < TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey >( queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: Omit< UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, "queryKey" | "queryFn" | "initialData" > & { initialData?: () => undefined; } ): ReturnType<typeof useQuery<TData, TError>> & { fetch: Function; }; } type IUseLazyQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey > = Omit< UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, "queryKey" | "queryFn" | "initialData" > & { initialData?: () => undefined; }; export const useLazyQuery: IUseLazyQuery = < TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey >( queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: IUseLazyQueryOptions<TQueryFnData, TError, TData, TQueryKey> ) => { const [enabled, setEnabled] = useState(false); const resolveRef = useRef<(value: unknown) => void>(); const query = useQuery(queryKey, queryFn, { ...options, enabled: enabled && (options?.enabled === undefined || options?.enabled), onSettled: () => { if (resolveRef.current) { resolveRef.current(null); resolveRef.current = undefined; } }, }); useEffect(() => { setEnabled(false); }, [query.data]); return { fetch: () => { setEnabled(true); return new Promise(async (resolve) => { resolveRef.current = resolve; while (resolveRef.current) { await new Promise((resolve) => setTimeout(resolve, 500)); } }); }, ...query, }; }; export const useFilteredRequest = < TQueryFuncParams extends object, TQueryFnData = unknown, TError = unknown, TData = TQueryFnData >( name: string, func: (params: TQueryFuncParams) => TQueryFnData, params: TQueryFuncParams, options?: IUseLazyQueryOptions<TQueryFnData, TError, TData> ) => { const { category, classification, selectedBrands } = usePageFilters(); const { filterRequestParams } = useFilterContext(); const { dataFinalStr, dataInicialStr } = useDateFilterContext(); const filterParams = useMemo(() => { return { marcas: selectedBrands.join(","), classificacoes: classification, categorias: category, data_final: dataFinalStr, data_inicio: dataInicialStr, ...filterRequestParams, }; }, [ selectedBrands, classification, category, dataFinalStr, dataInicialStr, filterRequestParams, ]); const queryFuncParams = useMemo(() => { return { ...params, ...filterParams }; }, [params, filterParams]); // 修正后的queryKey构造,过滤空值并明确类型 const queryKey = useMemo(() => { return [ name, ...Object.values(queryFuncParams) .filter((val): val is string => typeof val === 'string') .sort() ]; }, [name, queryFuncParams]); return useLazyQuery( queryKey, () => func(queryFuncParams), options ); };
错误提示
Argument of type '(string | null | undefined)[]' is not assignable to parameter of type 'TQueryKey'.
'(string | null | undefined)[]' is assignable to the constraint of type 'TQueryKey', but 'TQueryKey' could be instantiated with a different subtype of constraint 'readonly unknown[]'
错误出现在原代码中useFilteredRequest调用useLazyQuery的queryKey参数处:
return useLazyQuery( [name, ...Object.values(queryFuncParams).sort()], () => func(queryFuncParams), options );
解决方案
错误原因
- 泛型顺序不匹配:
useLazyQuery的实现泛型顺序与接口定义不一致,导致类型推导逻辑混乱。 - 不必要的泛型参数:
useFilteredRequest允许外部指定TQueryKey,但内部构造的queryKey是固定格式数组,与外部指定的泛型类型无法兼容。 - queryKey类型含空值:
Object.values(queryFuncParams)可能返回null/undefined,导致queryKey类型不够明确,无法匹配泛型约束。
修复步骤
- 对齐
useLazyQuery泛型顺序:让实现的泛型顺序与接口IUseLazyQuery完全一致,确保类型推导正确。 - 移除
useFilteredRequest的TQueryKey泛型:该钩子的queryKey由内部构造,不需要外部自定义类型,移除后避免类型冲突。 - 优化queryKey类型:过滤掉
null/undefined值,并用类型断言确保剩余值为字符串,让queryKey类型更明确。
内容的提问来源于stack exchange,提问作者Arthur Nascimento
相关产品推荐
相关产品推荐

