Arnas Dičkus
Arnas Dičkus

Reputation: 677

How to validate Yup files uploads with typescript

I need for my profileImgFile to be required and I want types in my validationSchema. Currently validation works as expected however typescript doesn't like validationSchema.

Error itself: Type 'ObjectSchema<Assign<ObjectShape, { name: RequiredStringSchema<string | undefined, AnyObject>; description: RequiredStringSchema<string | undefined, AnyObject>; profileImgFile: MixedSchema<...>; }>, AnyObject, TypeOfShape<...>, AssertsShape<...>>' is not assignable to type 'ObjectSchemaOf<IForm, never>'.

From what I read in documentation common consensus is to use yup mixed. Another solution is to use Yup.object() but than you have to deal with file properties.

profileImgFile: Yup.mixed().required("Required")

// Another possible solution
profileImgFile: Yup.object({
  // somehow spread all files Properties, or FileList properties.
}).required("Required")

Anyways here's working example code-sandbox

interface IForm {
  name: string;
  description: string;
  profileImgFile: File;
}

 const validationSchema: Yup.SchemaOf<IForm> = Yup.object().shape({
    name: Yup.string().required("Required"),
    description: Yup.string().required("Required"),
    profileImgFile: Yup.mixed().required("Required")
  });


const {
    register,
    handleSubmit,
    control,
    reset,
    formState: { errors }
  } = useForm<IForm>({
    resolver: yupResolver(validationSchema)
  });

<Controller
   name="profileImgFile"
   control={control}
   render={({ field }) => (
    <input
    ref={fileInputRef}
     type="file"
     id="avatar"
     onChange={(val) => {
       field?.onChange(val?.target?.files);
     }}
     name="profileImgFile"
     accept="image/png, image/jpeg"
   />
  )}
/>

Upvotes: 2

Views: 6076

Answers (2)

sylvain s
sylvain s

Reputation: 384

Another solution is simply to put the type File after mixed , this way you don't need to type all the yup schema :

  document: Yup.mixed<File>() 👈
    .required()
    .test('fileSize', i18n.t('medical.form.max.size'), (value) => {
      console.log('*******', value);
      if (!value) {
        return true;
      }
      return value.size < 1000000;
    }),

Upvotes: 2

Roar S.
Roar S.

Reputation: 10939

If we start with a basic file input, it will bind with a FileList instance.

<Form.Group
    controlId="profileImgFile">
    <Form.Control
        {...register("profileImgFile")}
        type="file"/>
    <Form.Control.Feedback type="invalid">{errors.profileImgFile?.message}</Form.Control.Feedback>
</Form.Group>

With this approach, profileImgFile has to be declared as FileList like shown below in IForm. I'm aware of that you want just a single file, but it should be fairly simple to deal with that downstream in your code.

interface IForm {
  name: string;
  description: string;
  profileImgFile: FileList;
}

After testing a lot of other approaches, I landed on a Yup.mixed().test() approach in my app like shown below. You can extend the .test() chain with other file checks (file size etc).

const validationSchema: Yup.SchemaOf<IForm> = Yup.object().shape({
    name: Yup.string().required("Required"),
    description: Yup.string().required("Required"),
    profileImgFile: Yup.mixed().test(
        "required", 
        "Please select a file", 
        (files: FileList) => files?.length > 0)
});

Upvotes: 1

Related Questions