I want to have the type checked in the onChange event of react-hook-form

36 Views Asked by At
const sampleSchema= z.object({
  sampleData: z.array(
    z.object({
      flag: z.boolean(),
      value: z.number()
    })
  ),
});


type FormInput = z.input<typeof sampleSchema>;

const SampleComponent = () => {
  const { control } = useFormContext<FormInput>();
  const { field } = useController({ control, name: 'sampleData' });

  return (
    <input
      onChange={(event) => {
        field.onChange([
          ...field.value,
          ...[
            {
              flag: true,
              // does not have value
            },
          ],
        ]);
      }}
    />
  );
};

In the schema, value is defined as not nullish, but it is not type checked because field.onChange() expects any[]

  1. Is it possible to pass explicit type to onChange?
  2. If this is not possible, is it possible to use eslint etc. to detect code that uses the Spread Operator to combine different types into one array as an error?
1

There are 1 best solutions below

0
Amir Ben Shimol On BEST ANSWER

Directly passing an explicit type to onChange is not supported out of the box because onChange is meant to handle any user input. However, you can enforce type checking indirectly by defining a type-safe wrapper function around your logic before calling onChange. Here's an example:

const SampleComponent = () => {
  const { control } = useFormContext<FormInput>();
  const { field } = useController({ control, name: 'sampleData' });

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = parseInt(event.target.value, 10);

    const newItem = {
      flag: true,
      value: newValue,
    };

    // Check against the Zod schema here if needed before calling onChange
    const result = sampleSchema.safeParse({
      sampleData: [
        ...field.value,
        newItem,
      ],
    });

    if (result.success) {
      field.onChange(result.data.sampleData);
    } else {
      console.error("Invalid data", result.error);
    }
  };

  return (
    <input onChange={handleChange} />
  );
};