react-drag-drop-files
react-drag-drop-files copied to clipboard
Narrow handle function types based on "multiple" prop
Hey! 👋🏼 Thanks a lot for implementing such a time-saver library!
When multiple
prop is set to a falsy value, it is guaranteed to have exactly 1 file. However the typings on onDrop
, onSelect
and handleChange
are widely typed to accommodate both cases. I'd expect them to be narrowed to accept File
as their first argument when multiple
is set to falsy value.
Current types:
<FileUploader
onDrop={file => {}}
// ^? (property) onDrop?: ((arg0: File | Array<File>) => void)
/>
<FileUploader
multiple={false}
onDrop={file => {}}
// ^? (property) onDrop?: ((arg0: File | Array<File>) => void)
/>
<FileUploader
multiple
onDrop={files => {}}
// ^? (property) onDrop?: ((arg0: File | Array<File>) => void)
/>
Expected narrowed types:
<FileUploader
onDrop={file => {}}
// ^? (property) onDrop?: ((file: File) => void)
/>
<FileUploader
multiple={false}
onDrop={file => {}}
// ^? (property) onDrop?: ((file: File) => void)
/>
<FileUploader
multiple
onDrop={files => {}}
// ^? (property) onDrop?: ((files: Array<File>) => void)
/>
This could be achieved by having Props implemented as a generic type. Theoretically with something like:
type FileFn<M extends boolean> = [boolean] extends [M]
? (arg: File | File[]) => void
: M extends true
? (files: File[]) => void
: (file: File) => void;
type Props<M extends boolean = false> = {
multiple?: M;
onDrop?: FileFn<M>;
onSelect?: FileFn<M>;
handleChange?: FileFn<M>;
// ... other props are omitted
};
function FileUploader<M extends boolean = false>(props: Props<M>) {
return <>{/* ... code is omitted. Returning props instead, to be able to test */}</>;
}
Tests:
type SingleTest = FileFn<false>;
// ^? type SingleTest = (file: File) => void;
type MultiTest = FileFn<true>;
// ^? type MultiTest = (files: File[]) => void
const dynamicValue = Math.random() >= 0.5;
<FileUploader multiple={dynamicValue} onDrop={} />;
// ^? (arg: File | File[]) => void
<FileUploader multiple handleChange={} />;
// ^? (property) handleChange?: ((files: File[]) => void)
<FileUploader multiple={false} handleChange={} />;
// ^? (property) handleChange?: ((file: File) => void)
<FileUploader handleChange={} />;
// ^? (property) handleChange?: ((file: File) => void)