Typescript: HTMLInputElement type "file" change event.target type (EventTarget) has no files.

Created on 7 Jun 2019  ·  12Comments  ·  Source: microsoft/TypeScript

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.

Bug lib.d.ts

Most helpful comment

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) {}

All 12 comments

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...
}

I suggest extending EventTarget for optional property files.

For those that come behind, I liked this solution:

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

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

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) {}

Don't put the type of event :)


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'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.

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;

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

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?

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.

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));
Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

wmaurer picture wmaurer  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

jbondc picture jbondc  ·  3Comments