TypeScript:如何使用泛型输入 react-query useMutation onError?

Posted

技术标签:

【中文标题】TypeScript:如何使用泛型输入 react-query useMutation onError?【英文标题】:TypeScript: How to type react-query useMutation onError with generic? 【发布时间】:2021-11-29 12:17:36 【问题描述】:

考虑一个自定义的useSignUp 钩子,其中mutate() 函数需要一些字段(在此示例中为nameemail):

export default function App() 
  const name = "David";
  const email = "david@gmail.com";
  const signupMutation = useSignUp();

  return (
    <button
      onClick=() => 
        signupMutation.mutate(
           name, email ,
          
            onSuccess: (result) => 
              ...
            ,
            onError: (result) => 
              // Wanted: result of type ErrorResult<"name" | "email">
            
          
        );
      
    >
      Sign Up
    </button>
  );

在这种情况下,我希望onError 的结果为ErrorResponse&lt;"name" | "email"&gt; 类型。但是,我得到了ErrorResponse&lt;string&gt;

这是为什么呢?我如何指示 TypeScript 根据传递的数据推断特定类型(即"name" | "email",而不是string)?

这是我输入useSignUp的方式:

type SuccessResponse = 
  userId: string;
;

type ErrorResponse<T> = 
  userId?: string;
  formErrors?: Array<
    field: T;
    type: string;
  >;
;

export const useSignUp = <T extends string>() => 
  return useMutation<SuccessResponse, ErrorResponse<T>, Record<T, string>>(
    (data) => 
      return new Promise((resolve, reject) => 
        // Some logic here to either return a success or an error response
        if (Math.random() > 0.5) 
          resolve(
            userId: "1234"
          );
         else 
          reject(
            formErrors: [
              
                field: "email", // I want TypeScript to complain if it's neither "name" nor "email"
                type: "ALREADY_EXISTS"
              
            ]
          );
        
      );
    
  );
;

【问题讨论】:

关于你的reject签名,参数不能更改。请参阅this 答案。 @MishaMoroshko 你能提供可重现的例子吗? 【参考方案1】:

你已经声明了一个带有通用错误字段的钩子,如果你不提供参数,它默认为一个字符串:

export const useSignUp = <T extends string>() => 
  return useMutation<SuccessResponse, ErrorResponse<T>, Record<T, string>>(...)

所以一定要覆盖它:

type Field = 'name' | 'email';

// ...

useSignUp<Field>();

如果要根据提交的值键入错误:

const mutatedValues = 
  name: "David",
  email: "david@gmail.com"
;
const signupMutation = useSignUp<keyof typeof mutatedValues>();
onError: (result) => 
  result.formErrors?.forEach((e) => 
    const  field  = e; // field is 'name' | 'email'
  );

【讨论】:

是否可以避免将泛型提供给useSignUp,而让TypeScript 以某种方式提供给infer @MishaMoroshko 理论上,我认为 Typescript 可以通过使来自 react-querymutate() 函数知道数据的关键,基于 mutatedValues 推断 onError 回调的参数,但图书馆不支持它。它们只允许您单独覆盖 TDataTError 需要在 useMutation 调用中定义类型。你不能让 useMutation 根据你最终传递给mutate 的内容来推断它的类型。要么按照此处的建议显式传递泛型,要么进行与用户名/电子邮件相关的特定突变(无泛型),或者将字段直接传递给 useMutation(如果可用),以便您可以从中推断:const signupMutation = useSignUp( name, email );。然后,您可以在没有参数的情况下调用.mutate(),并关闭您传递给 useMutation 的内容。然后,推理应该起作用了。

以上是关于TypeScript:如何使用泛型输入 react-query useMutation onError?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?

如何使用泛型缩小 TypeScript 联合类型

如何使用带有 Typescript 的泛型将中继(graphql)连接映射到边缘节点

React Typescript 泛型类型重载(例如 useState)

TypeScript泛型

为啥在 Typescript 泛型中使用 '&'