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

TypeScript类型参数推断异常:如何让TParams正确推断为MyVariables

问题:TypeScript泛型类型推断异常导致mutate调用报错

我定义了如下泛型函数:

const useInfiniteMutations: <TData, TPage, TParams>(...: {
  getNextPageParam?: (lastPageParam: TPage | undefined) => TPage | undefined;
  mutationFn: (variables: { pageParam: TPage } & TParams) => Promise<TData>;
  onSuccess?: (data: TData) => void;
}) => void

传入参数时:

type MyVariables = {
  name: string;
};

const { mutate } = useInfiniteMutations({
  getNextPageParam: (lastPageParam = -1) => lastPageParam + 1,
  mutationFn: async ({
    name,
    pageParam,
  }: MyVariables & { pageParam: number }) => {
    const result = { name, pageParam };
    return result;
  },
});

发现TypeScript的类型推断结果不符合预期:

const useInfiniteMutations: <{
    name: string;
    pageParam: number;
}, number, MyVariables & {
    pageParam: number;
}>

这导致调用mutate时报错:

const variables: MyVariables = {
  name: "foo",
};

mutate(variables); // Property 'pageParam' is missing in type 'MyVariables' but required in type '{ pageParam: number; }'.typescript(2345)

完整最小可复现代码:

import { useEffect, useState } from "react";

export const useInfiniteMutations = <TData, TPage, TParams>({
  getNextPageParam,
  mutationFn,
  onSuccess,
}: {
  getNextPageParam?: (lastPageParam: TPage | undefined) => TPage | undefined;
  mutationFn: (variables: { pageParam: TPage } & TParams) => Promise<TData>;
  onSuccess?: (data: TData) => void;
}) => {
  const [params, setParams] = useState<TParams>();

  const [nextPageParam, setNextPageParam] = useState<TPage>();

  const mutate = (newParams: TParams) => {
    const newNextPageParam = getNextPageParam?.(undefined);
    if (newNextPageParam === undefined) {
      return;
    }

    setNextPageParam(newNextPageParam);
    setParams(newParams);
  };

  useEffect(() => {
    if (params === undefined || nextPageParam === undefined) {
      return;
    }

    mutationFn({
      ...params,
      pageParam: nextPageParam,
    }).then((data) => {
      onSuccess?.(data);
    });
  }, [nextPageParam]);

  return {
    mutate,
  };
};

export function demo() {
  type MyVariables = {
    name: string;
  };

  const { mutate } = useInfiniteMutations({
    getNextPageParam: (lastPageParam = -1) => lastPageParam + 1,
    mutationFn: async ({
      name,
      pageParam,
    }: MyVariables & { pageParam: number }) => {
      const result = { name, pageParam };

      return result;
    },
  });

  const variables: MyVariables = {
    name: "foo",
  };

  mutate(variables); // 报错:Property 'pageParam' is missing in type 'MyVariables' but required in type '{ pageParam: number; }'.typescript(2345)
}

原因分析

TypeScript在推断TParams时,会直接从mutationFn的参数类型MyVariables & { pageParam: number }反向推导,无法识别pageParam是框架内部自动注入的参数,不属于用户传入的TParams范畴。交叉类型的写法让TS无法区分用户参数和内部参数,导致TParams被错误推断为包含pageParam的交叉类型。

解决方案

核心是明确区分用户参数和内部注入的pageParam,避免交叉类型造成的推断混淆,通过泛型调整或重载引导正确推断。

方案1:调整泛型参数顺序并简化类型定义

将TParams放在泛型参数首位,让TS优先推断用户参数,同时移除mutationFn参数的手动类型注解,让TS自动推导:

export const useInfiniteMutations = <TParams, TPage, TData = unknown>({
  getNextPageParam,
  mutationFn,
  onSuccess,
}: {
  getNextPageParam?: (lastPageParam: TPage | undefined) => TPage | undefined;
  mutationFn: (variables: TParams & { pageParam: TPage }) => Promise<TData>;
  onSuccess?: (data: TData) => void;
}) => {
  // 原有逻辑保持不变
};

调用时无需手动指定mutationFn的参数类型,TS会自动推断TParams为MyVariables:

export function demo() {
  type MyVariables = {
    name: string;
  };

  const { mutate } = useInfiniteMutations({
    getNextPageParam: (lastPageParam = -1) => lastPageParam + 1,
    mutationFn: async ({ name, pageParam }) => {
      const result = { name, pageParam };
      return result;
    },
  });

  const variables: MyVariables = {
    name: "foo",
  };

  mutate(variables); // 无报错
}

方案2:使用函数重载明确类型边界

通过函数重载,明确区分用户传入的参数结构和内部组合后的参数结构,强制TS做出正确推断:

// 重载签名:清晰定义用户参数与内部参数的组合
export function useInfiniteMutations<TParams, TPage, TData>(
  options: {
    getNextPageParam?: (lastPageParam: TPage | undefined) => TPage | undefined;
    mutationFn: (variables: TParams & { pageParam: TPage }) => Promise<TData>;
    onSuccess?: (data: TData) => void;
  }
): { mutate: (params: TParams) => void };

// 实现签名
export function useInfiniteMutations<TParams, TPage, TData>({
  getNextPageParam,
  mutationFn,
  onSuccess,
}: {
  getNextPageParam?: (lastPageParam: TPage | undefined) => TPage | undefined;
  mutationFn: (variables: TParams & { pageParam: TPage }) => Promise<TData>;
  onSuccess?: (data: TData) => void;
}) {
  // 原有逻辑保持不变
}

这种方式同样能让TS正确识别TParams为用户定义的MyVariables,避免错误包含pageParam。

原理说明

TypeScript泛型推断基于传入参数反向推导,交叉类型{ pageParam: TPage } & TParams会让TS无法自动拆分用户参数和内部参数。调整泛型参数顺序(优先推断用户参数)或使用重载明确类型边界,能引导TS做出符合预期的类型推断。

内容的提问来源于stack exchange,提问作者Changdae Park

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 12:48:10