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
相关产品推荐
相关产品推荐

