You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
);

解决方案

错误原因

  1. 泛型顺序不匹配:useLazyQuery的实现泛型顺序与接口定义不一致,导致类型推导逻辑混乱。
  2. 不必要的泛型参数:useFilteredRequest允许外部指定TQueryKey,但内部构造的queryKey是固定格式数组,与外部指定的泛型类型无法兼容。
  3. queryKey类型含空值:Object.values(queryFuncParams)可能返回null/undefined,导致queryKey类型不够明确,无法匹配泛型约束。

修复步骤

  1. 对齐useLazyQuery泛型顺序:让实现的泛型顺序与接口IUseLazyQuery完全一致,确保类型推导正确。
  2. 移除useFilteredRequest的TQueryKey泛型:该钩子的queryKey由内部构造,不需要外部自定义类型,移除后避免类型冲突。
  3. 优化queryKey类型:过滤掉null/undefined值,并用类型断言确保剩余值为字符串,让queryKey类型更明确。

内容的提问来源于stack exchange,提问作者Arthur Nascimento

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 23:45:40