ค้นหาเว็บไซต์

reactjs - TypeScript: วิธีการกำหนดชนิด react-query useMutation onError ด้วย generic?


พิจารณา custom useSignUp hook ซึ่งฟังก์ชัน mutate() รับบางฟิลด์ (name และ email ในตัวอย่างนี้):

export default function App() {
  const name = "David";
  const email = "[email ";
  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<"name" | "email"> ในกรณีนี้ แต่ผมได้รับ ErrorResponse<string>

ทำไมถึงเป็นเช่นนั้น? ฉันจะสามารถสั่ง 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"
              }
            ]
          });
        }
      });
    }
  );
};

คำตอบ #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: "[email "
};
const signupMutation = useSignUp<keyof typeof mutatedValues>();
onError: (result) => {
  result.formErrors?.forEach((e) => {
    const { field } = e; // field is 'name' | 'email'
  });
}

บทความที่เกี่ยวข้อง