15

I just started with TypeScript, so please bear in mind. I am trying to implement a simple file upload in React/TS. In general, I don't think I understand how to initialize objects, like in useState, and handle the possibilities properly. For example, consider this code where I am running a function when a user clicks an Upload button and I try to read the file from state and put in the formData to send it to my api endpoint):


const [fileSelected, setFileSelected] = React.useState<File>() // also tried <string | Blob>

const handleImageChange = function (e: React.ChangeEvent<HTMLInputElement>) {
    const fileList = e.target.files;

    if (!fileList) return;
    
    setFileSelected(fileList[0]);
  };

   const uploadFile = function (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
    const formData = new FormData();
    formData.append("image", fileSelected, fileSelected.name);

     // line above ^ gives me the error below
  };
}

Argument of type 'string | Blob | undefined' is not assignable to parameter of type 'string | Blob'. Type 'undefined' is not assignable to type 'string | Blob'.ts(2345)

How are you supposed to initialize your objects in useState? And if you don't initialize, like here, do you have to keep checking for null to keep the compiler happy? I just added a check in UploadFile to see if fileSelected is null. But then I had to do it again to reference fileSelected.name.

So for objects, particularly something like this File type, how should you handle initializing it? But also, in general, how to handle object types?

Not that it matters, but here's the markup part:

        <Container className={classes.container}>
          <label htmlFor="photo">
            <input
              accept="image/*"
              style={{ display: "none" }}
              id="photo"
              name="photo"
              type="file"
              multiple={false}
              onChange={handleImageChange}
            />

            <Button
              className={classes.buttons}
              component="span"
              variant="contained"
              onClick={uploadFile}
            >
              Choose Picture
            </Button>
          </label>
        </Container>
MattoMK
  • 609
  • 1
  • 8
  • 25

2 Answers2

14

When you call useState without setting an initial/default value then the type will include undefined in addition to the expected type. Here you've used the generic <File> to tell useState what type to expect. But because you initiated it without setting a value, the type of fileSelected becomes File | undefined.

This is fine because it accurately represents the reality that there will not always be a File object in the state. But it does mean that we have to check the value of fileSelected before using it. Just add an if statement and your formData.append() call won't have any issues at runtime or in the typescript compiler. The interface File extends Blob so there is no issue assigning it to string | Blob after we've ruled out the possibility of undefined.

const uploadFile = function (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
    if (fileSelected) {
        const formData = new FormData();
        formData.append("image", fileSelected, fileSelected.name);
    }
};

Playground Link

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • After I posted this I did put in ' |undefined ' so that makes sense now that I think about it. It was expecting it to be a File, period. So a File | undefined and then a check makes perfect sense. Thank you again :) – MattoMK Oct 23 '20 at 13:45
  • And thanks for the playground!. I didn't know that allowed you to use React. And to see the emitted javascript is very cool. – MattoMK Oct 23 '20 at 15:37
0

This method worked for me:

const handleChange = (event) => {
const file = event.target.files[0]
//console.log(file)
setFile({
    picturePreview: URL.createObjectURL(event.target.files[0]),
    pictureAsFile: event.target.files[0]
})}

And you need to append form data like:

formData.append('file_attachment', file.pictureAsFile)

Working example here: https://youtu.be/yTGXNvHQ4BQ

Inderjeet
  • 1
  • 4