1

I am getting the following error while I define the type of file object inside state, Argument of type 'null' is not assignable to parameter of type 'File | (() => File)'.ts. I am having a upload component, that basically takes care of selecting the file and sending its value to the parent component. I am trying to this in a typescript way and I am unable to get rid of this.

Link to the sandbox: https://codesandbox.io/s/react-hook-upload-oxqdp2?file=/src/Upload.tsx

Upload

import * as React from "react";
import { Button } from "@material-ui/core";
import { useState } from "react";

interface UploaderProps {
  fileType?: string | AcceptedFileType[];
}

enum AcceptedFileType {
  Text = ".txt",
  Gif = ".gif",
  Jpeg = ".jpg",
  Png = ".png",
  Doc = ".doc",
  AllImages = "image/*",
  AllVideos = "video/*",
  AllAudios = "audio/*"
}

export const Upload = (props: UploaderProps): JSX.Element => {
  const { fileType } = props;
  const acceptedFormats: string | AcceptedFileType[] =
    typeof fileType === "string"
      ? fileType
      : Array.isArray(fileType)
      ? fileType?.join(",")
      : AcceptedFileType.Text;
  const [selectedFiles, setSelectedFiles] = useState<File>(null);

  const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSelectedFiles(event?.target?.files?.[0]);
  };

  const onUpload = () => {
    // eslint-disable-next-line no-console
    console.log(selectedFiles);
  };

  return (
    <>
      <Button
        variant="contained"
        component="label"
        style={{ textTransform: "none" }}
      >
        <input
          hidden
          type="file"
          accept={acceptedFormats}
          onChange={handleFileSelect}
        />
        <span> Choose file to upload</span>
      </Button>
      <Button
        color="primary"
        disabled={!selectedFiles}
        style={{ textTransform: "none" }}
        onClick={onUpload}
      >
        Upload
      </Button>
    </>
  );
};

Can someone help?

5
  • 1
    The safest would be useState<File | null>(null);. Commented Aug 30, 2022 at 15:20
  • It throws, Argument of type 'File | undefined' is not assignable to parameter of type 'SetStateAction<File | null>'. Type 'undefined' is not assignable to type 'SetStateAction<File | null>' Commented Aug 30, 2022 at 15:23
  • Well then, decide what you want. null or undefined to represent the missing file. When you setSelectedFiles, the value you give it can be undefined if the input is empty. Commented Aug 30, 2022 at 15:24
  • Or just const [selectedFiles, setSelectedFiles] = useState<File>(); This sets the type to File | undefined which is most probably what you want. Commented Aug 30, 2022 at 15:24
  • @kelly, what is the recommended way? Commented Aug 30, 2022 at 15:26

1 Answer 1

4

You should probably stick to undefined:

const [selectedFiles, setSelectedFiles] = useState<File | undefined>(undefined);

const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
  setSelectedFiles(event?.target?.files?.[0]);
};

This is because when you set it, the value has the type File | undefined.

This will also force you to check the value before using it, since it could be undefined.

If you know that there will be a file, you can use an assertion with !:

setSelectedFiles(event?.target?.files?.[0]!);

and maybe keep the type as File | null, but even then I'd still use File | undefined because null is a bit quirky in JavaScript (typeof null === "object").

Sign up to request clarification or add additional context in comments.

2 Comments

useState<File|undefined>(undefined); is unnecessarily verbose. useState<File>(); is more concise and means exactly the same. useState is defined as function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];.
Yes, but it also forces you to perform a sanity check every single time you look at the code. It is my preference to be as explicit as possible when it comes to types. It doesn't help that the first overload you see for useState has a completely different signature in which the call useState<File>() would not match.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.