Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTMLInputElement type "file" change event.target type (EventTarget) has no files. #31816

Closed
Domvel opened this issue Jun 7, 2019 · 25 comments
Closed
Labels
Bug A bug in TypeScript Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript
Milestone

Comments

@Domvel
Copy link

Domvel commented Jun 7, 2019

Is there an event type for the HTMLInputElement change event? Currently is automatically resolved as general Event > EventTarget. Which has no files property. see FileList.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement

Example:

const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.addEventListener('change', (event: Event) => {
  handleFileLoad(event.target.files); // Error: files does not exist on EventTarget.
});
document.body.appendChild(this.fileInput);

tsconfig.json (typescript 3.4.5):

{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": true,
    "sourceMap": true,
    "module": "esnext",
    "target": "es5",
    "moduleResolution": "node",
    "strict": true
  },
  "exclude": ["node_modules", "dist"]
}

If not, this is a feature request. :) e.g.

@Domvel Domvel changed the title HTMLInputElement type "file" change event type? HTMLInputElement type "file" change event.target type (EventTarget) has no files. Jun 7, 2019
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jun 13, 2019
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jun 13, 2019
@RyanCavanaugh RyanCavanaugh added the Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript label Jun 13, 2019
@leonelngande
Copy link

As per this Stackoverflow answer, you can cast event.target as HTMLInputElement to get around this.

document.getElementById("customimage").onchange= function(e: Event) {
    let file = (<HTMLInputElement>e.target).files[0];
    //rest of your code...
}

@DDeme
Copy link

DDeme commented Jan 20, 2020

I suggest extending EventTarget for optional property files.

@ZackDeRose
Copy link

For those that come behind, I liked this solution:

export type FileEventTarget = EventTarget & { files: FileList };

const fileInputNativeElement =
  fromEvent(fileInputNativeElement, 'change') as Observable<{ target: FileEventTarget }>;

@amatiasq
Copy link

amatiasq commented Jun 1, 2020

I always wondered why Event is not generic.

interface Event<T = EventTarget> {
  target: T;
  // ...
}

So we define the type where we need it.

function myEventListener(event: Event<HTMLInputElement>) {
  console.log(event.target.files);
}

function otherEventListener(event: Event<HTMLAnchorElement>) {
  console.log(event.target.href);
}

This can be made backwards compatible with default generics:

// still works
function myEventListener(event: Event) {}

@ghost
Copy link

ghost commented Jun 4, 2020


Don't put the type of event :)


@infacto
Copy link

infacto commented Jun 18, 2020

I'm working with TypeScript ~3.8.3 and HTMLInputElement contains now files (FileList).

const target = event.target as HTMLInputElement;
const files = target.files;

See sources 👍

@ghost
Copy link

ghost commented Jun 18, 2020

I'm working with TypeScript ~3.8.3 and HTMLInputElement contains now files (FileList).

const target = event.target as HTMLInputElement;

const files = target.files;

See sources 👍

I was have the same problem and it worked when I quit use the type of element. Thank you for answer me.

@Domvel
Copy link
Author

Domvel commented Jun 19, 2020

That's correct. It also exists in TypeScript 3.4.5. It also exists in the old version TypeScript 2.0
I don't know what I was thinking back then. ... 🤦‍♂️

I close this issue as fixed. Thanks. The type HTMLInputElement has the property files which is nullable FileList. All fine.

const target = event.target as HTMLInputElement;
const files = target.files;

@Domvel Domvel closed this as completed Jun 19, 2020
@amatiasq
Copy link

I understood the issue as event.target doesn't have files and that's because it requires casting.
We could take this opportunity to remove the casting step using generics

@Domvel
Copy link
Author

Domvel commented Jun 19, 2020

const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.addEventListener('change', event => {
  console.log(event.target.files);
});

This is acual resolved by the TypeScript in VS Code. (just hover the method to see it.)

HTMLInputElement.addEventListener<"change">(
  type: "change", 
  listener: (this: HTMLInputElement, ev: Event) => any, 
  options?: boolean | AddEventListenerOptions
): void

Means that the HTMLInputElement is detected and maybe available for a generic event type. 🤔
Is there already a generic Event type? But anyway it is not resolved in this generic type.
It's a good idea. If TypeScript detects the context of the event listener - in this case - why not?
But note that in my initial post, I use the fixed type Event for the event variable.

So the Event type of a addEventListener should detect the target type (generic) if possible.
Any examples or ideas? Any expertise of a member or constributor?

@stof
Copy link

stof commented Aug 12, 2020

the issue with making target generic here is related to bubbling events. Adding a listener somewhere will generally not guarantee you the type of target as it can actually be dispatched on a child node. For instance, clicking on a button might actually click on an icon inside that button instead.

@phfoxer
Copy link

phfoxer commented Sep 7, 2020

As per this Stackoverflow answer, you can cast event.target as HTMLInputElement to get around this.

document.getElementById("customimage").onchange= function(e: Event) {
    let file = (<HTMLInputElement>e.target).files[0];
    //rest of your code...
}
 let files:FileList = (<FileList>(<HTMLInputElement>e.target).files);
console.log(files.item(0));

@egorovsa
Copy link

Just

function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
    console.log(event.target.files);
}

@egorovsa
Copy link

egorovsa commented Nov 7, 2021

How can I tell typescript that I exactly will get a file in this example? Does anybody know?

const upload = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();
    const target = e.currentTarget as HTMLInputElement;
    const file = target.files[0];
    console.log(file)
};

Is says on this line const file = target.files[0] that object is possibly null

Just check it through: "?"
const file = target.files?.[0];
however pay attention that the type will be File | undefined

@stevenKirill
Copy link

How can I tell typescript that I exactly will get a file in this example? Does anybody know?

const upload = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();
    const target = e.currentTarget as HTMLInputElement;
    const file = target.files[0];
    console.log(file)
};

Is says on this line const file = target.files[0] that object is possibly null

@Dassderdie
Copy link

@stevenKirill
You could use the non-null-assertion-operator-postfix, like this const file = target.files![0]; if you are completely sure that files is defined.

@hejuhenryk
Copy link

How can I tell typescript that I exactly will get a file in this example? Does anybody know?

const upload = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();
    const target = e.currentTarget as HTMLInputElement;
    const file = target.files[0];
    console.log(file)
};

Is says on this line const file = target.files[0] that object is possibly null

then check if it is not
if (e.target.files && e.target.files.length > 0) { const file = e.target.files[0]

@ritikbanger
Copy link

React.ChangeEvent<HTMLInputElement> this doesn't work in case of filelist. It gives the error: Type 'FileList' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators

@umbrashia
Copy link

umbrashia commented Nov 17, 2022

use "Types" for solution of that problem
export type TGenericEvent<T> = Event & { target: T };

use like this

eventCall(evt?: TGenericEvent<HTMLInputElement>): void {

    console.log("event",evt?.target.value);

  }

image

@AntonyStone1
Copy link

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

@guiled
Copy link

guiled commented Feb 12, 2023

@umbrashia and @amatiasq suggestion should be implemented natively in ts

@rcollette
Copy link

Casting isn't helpful for unit tests. Having the Event type be a generic would be more useful.

image

@vincerubinetti
Copy link

I ended up having to do this:

type FileEvent = ChangeEvent<HTMLInputElement> & {
  target: EventTarget & { files: FileList };
};

🤪

@KraXen72
Copy link

KraXen72 commented Aug 11, 2023

after many iterations, this is what worked for me:

async function upload(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
	if (!e.currentTarget || !e.currentTarget.files) return;
	const file = e.currentTarget.files[0];
	//...
}

i'm on typescript@^5.1.6, using svelte's on:change={upload} but unsure if that's relevant

@cassepipe
Copy link

cassepipe commented Aug 14, 2023

It's kind sad to have to track down what the DOM events types are over there : https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/f85d145348fc853bc7fecea5e5925c66a2e0e0b6/inputfiles/addedTypes.jsonc#L193

Only to find out that the event you need does not exist in some github issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript
Projects
None yet
Development

No branches or pull requests