reactjstypescripttypescript-genericsreact-typescriptreact-dropzone

How to use TypeScript generics to extend a library function with React's useCallback


I am creating a component with react-dropzone. react-dropzone provides a useDropzone() which takes a object for arguments, e.g. useDropzone({ onDrop }).

CodeSandbox example is here

useDropzone's type is:

export function useDropzone(options?: DropzoneOptions): DropzoneState;

DropzoneOptions are

export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, PropTypes> & {
  onDrop?: <T extends File>(acceptedFiles: T[], fileRejections: FileRejection[], event: DropEvent) => void;
};

How do I type acceptedFiles (instead of any) by typing the generic T in a useCallback?

  const onDrop = useCallback((acceptedFiles: any) => {
    acceptedFiles.forEach((file) => {
      console.log(file)
    })
  }, [])

Solution

  • You can manually type the callback function

    const onDrop = useCallback(<T extends File>(acceptedFiles: T[]) => {
      acceptedFiles.forEach((file) => {
        console.log(file)
      })
    }, [])
    

    Or, you can directly use the type provided by the react-dropzone library

    type OnDropType = NonNullable<DropzoneOptions['onDrop']>;
    
    const onDrop = useCallback<OnDropType>((acceptedFiles) => {
      acceptedFiles.forEach((file) => {
        console.log(file)
      })
    }, [])
    

    NonNullable is needed inside useCallback because useCallback doesn't accept undefined as function type.