How to create a type for file type `Record<string, File>` in zod
Asked Answered
G

5

11

I use input type="file" and instead of saving data in FileData I use just plain object and created a type for it: Record<string, File>. It works well, but when I try to create a validator with zod – I don't get how to create the same type for it.

The usage:

import { object as zodObject, string, number } from 'zod';
import { useValidators } from '../some';

const useValidation = () => {
  const { createResolver } = useValidators();
  
  return {
    resolver: createResolver(
      zodObject({
        name: string(),
        price: number(),
        files: ???,  
      })
    ),
  };
}; 

The doc is quite excessive, but I could not find any example for my case: https://github.com/colinhacks/zod

The usage of input:


const App = () => {
  const [files, setFiles] = useState<Record<string, File>>({}); 

  return (
    <input 
      type="file" 
      onChange={event => {
        const files = event.target.files;
        const newFiles: Record<string, File> = {};
        const keys = Object.keys(files);
        
        for(let i = 0; i < keys.length; i++) {
          const file = newFiles[key];
          newFiles[file.name] = file;
        }

        setFiles(newFiles);
      }}
    />

  )

}

Glyptic answered 20/12, 2021 at 11:20 Comment(2)
How is your File type looks like?Nonpartisan
@nemesv, I have added a simple example of usage input. So, the main case to have this type to set file.name as a key of object to have a fast access for updating and deleting files. Cause input should store multiple files and user can add or remove some of the files.Glyptic
H
13

Maybe z.instanceof can solve it?

If you want to validate the File type ...

z.instanceof (File)

Or, for FileList type...

z.instanceof (FileList)


https://github.com/colinhacks/zod/issues/387

Horsepowerhour answered 26/4, 2022 at 8:37 Comment(3)
This is the correct answer. Also, another way of defining your schemas is to just import { z } from "zod" instead of importing zod methods individuallyDrugge
If you get a an error about File not being a valid reference, use z.custom<File>().Punkie
@TomášHübelbauer as the doc suggests, z.custom<File>() will perform no validation. We need to pass a validation function to this and the code might look like z.custom<File>((file) => file instanceof File)Scorcher
O
1
  requestFiles: z
.array(
  z
    .instanceof(File)
    .refine((file) => file.size < 2 * 1024 * 1024, 'File size must be less than 2MB'),
)
.min(1, 'At least 1 file is required')
.refine(
  (files) => files.every((file) => file.size < 2 * 1024 * 1024),
  'File size must be less than 2MB',
),
        <FormField
          name="requestFiles"
          control={form.control}
          render={({ field }) => (
            <FormItem className="md:col-span-4 col-span-1">
              <FormLabel>Attach Files</FormLabel>
              <FormControl>
                <Input
                  type="file"
                  className=" border-slate-300"
                  onChange={(e) => {
                    // Convert the FileList to an array and update the form state
                    const filesArray = Array.from(e.target.files || []);
                    field.onChange(filesArray);
                  }}
                  multiple // Allow multiple file selection
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
Ovine answered 31/1 at 5:53 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Tonettetoney
T
0

try this:

    z.object({
        file: z
         .any()
         .refine((files) => files?.length == 1, "File is required.")  
    })

and then use other file properties in refine to filter it as u need like:

  • files?.[0]?.size
  • files?.[0]?.type
Testosterone answered 11/6, 2023 at 10:39 Comment(0)
G
-2

My colleague has found the solution:

// useValidation file
import { object as zodObject, string, number, record, any } from 'zod';
import { useValidators } from '../some';

const useValidation = () => {
  const { createResolver } = useValidators();
  
  return {
    resolver: createResolver(
      zodObject({
        name: string(),
        price: number(),
        files: record(any()).nullable(),  
      })
    ),
  };
};  

And in the file that uses object with file data we use it in this way:

// fileInput on `onRemove` action return `fileName: string` and my file object looks like this:  { fileName: File } 

const [files, setFiles] = useState<Record<string, File> | null>(null);

<FileInput 
  onChange={(fileName) => {
    const updatedFiles = { ...files as Record<string, File> }; 
    delete updatedFiles[fileName];
    setFiles(updatedFiles);
  }}
/>
Glyptic answered 21/12, 2021 at 15:27 Comment(0)
S
-2

zodSchema:

  file:
    typeof window === "undefined"
      ? z.string()
      : record(any()).nullable(),

initialValues:

file: "undefined",
Skeens answered 11/4, 2022 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.