407

I am using TypeScript Version 2 for an Angular 2 component code.

I am getting error "Property 'value' does not exist on type 'EventTarget'" for below code, what could be the solution. Thanks!

e.target.value.match(/\S+/g) || []).length

import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'text-editor',
  template: `
    <textarea (keyup)="emitWordCount($event)"></textarea>
  `
})
export class TextEditorComponent {
  @Output() countUpdate = new EventEmitter<number>();

  emitWordCount(e: Event) {
    this.countUpdate.emit(
            (e.target.value.match(/\S+/g) || []).length);
  }
}
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
user584018
  • 10,186
  • 15
  • 74
  • 160
  • Does this answer your question? [The property 'value' does not exist on value of type 'HTMLElement'](https://stackoverflow.com/questions/12989741/the-property-value-does-not-exist-on-value-of-type-htmlelement) – miken32 May 31 '21 at 20:01
  • 2
    https://www.tektutorialshub.com/angular/property-value-does-not-exist-on-type-eventtarget-error-in-angular/ I think this will answer the question – ColdFire Oct 11 '21 at 13:33

19 Answers19

482

You need to explicitly tell TypeScript the type of the HTMLElement which is your target.

The way to do it is using a generic type to cast it to a proper type:

this.countUpdate.emit((<HTMLTextAreaElement>e.target).value./*...*/)

or (as you like)

this.countUpdate.emit((e.target as HTMLTextAreaElement).value./*...*/)

or (again, matter of preference)

const target = e.target as HTMLTextAreaElement;

this.countUpdate.emit(target.value./*...*/)

This will let TypeScript know that the element is a textarea and it will know of the value property.

The same could be done with any kind of HTML element, whenever you give TypeScript a bit more information about their types it pays you back with proper hints and of course less errors.

To make it easier for the future you might want to directly define an event with the type of its target:

// create a new type HTMLElementEvent that has a target of type you pass
// type T must be a HTMLElement (e.g. HTMLTextAreaElement extends HTMLElement)
type HTMLElementEvent<T extends HTMLElement> = Event & {
  target: T; 
  // probably you might want to add the currentTarget as well
  // currentTarget: T;
}

// use it instead of Event
let e: HTMLElementEvent<HTMLTextAreaElement>;

console.log(e.target.value);

// or in the context of the given example
emitWordCount(e: HTMLElementEvent<HTMLTextAreaElement>) {
  this.countUpdate.emit(e.target.value);
}
smnbbrv
  • 23,502
  • 9
  • 78
  • 109
  • 1
    @smnbbrv my case is an img file location then display the img, based on [SO](https://stackoverflow.com/questions/47067249/how-can-i-display-an-image-using-typescript-and-angular-4) Template: `
    ` Component: `... this.url = event.target.result;` Sometime works sometimes doesn't, when it's not err is `error TS2339: Property 'result' does not exist on type 'EventTarget'` As you suggested tell TS more about it, in the place `HTMLTextAreaElement` I tried `HTMLInputElement` then `target.value` no more err but image not displaying.
    – Jeb50 Jul 28 '18 at 18:57
  • I was surprised to see that you couldn't pass a type into the `Event` type. You should really be able to use `Event` as a type. – RonanCodes Nov 25 '19 at 14:00
  • @RoRo event has following similar properties: `target`, `currentTarget` and `srcElement`; one would need to type 3 generic types; even if they use default types e.g. `Event` for the mentioned above it could be more uncomfortable to use than the simple `as` statement. I could also imagine a potential holywar for what should be first generic: `target` or `currentTarget`. Additionally, many libraries abuse HTML event and can potentially put anything they want in the mentioned properties. Probably these are the reasons why they did not do it as built-in generics – smnbbrv Jan 28 '20 at 08:20
  • For my ion-searchbar, I am using `(ionChangeEvent.target as HTMLIonInputElement).value as string` – Cloud Jun 02 '20 at 23:57
  • 9
    Why does it need to be explicit? This is such a waste of time, DOM events are one of the biggest drawbacks of using TS for me, I'm always looking for TS definitions on SO. – Vadorequest Mar 28 '21 at 17:24
  • @Vadorequest this is very simple to answer: it is not possible to make it implicit. There is an Event type and you can never know what is the source of the event, you can only know it in the runtime. Here you actually say "I believe this target is HTMLTextAreaElement". TypeScript compiler has no way to extract this info from anywhere – smnbbrv Jun 10 '22 at 21:29
  • Yeah, my point was more like "why do I need to care about this? Because I really don't want to waste my time on this kind of detail". – Vadorequest Jun 11 '22 at 14:42
  • I get the error on Angular template: `Argument of type 'MouseEvent' is not assignable to parameter of type 'HTMLElementEvent'.` – Leonardo Rick Aug 19 '22 at 11:31
  • @LeonardoRick try changing type `HTMLElementEvent = Event & {` to `type HTMLElementEvent = MouseEvent & {` (MouseEvent) – smnbbrv Aug 20 '22 at 18:56
  • @smnbbrv it do not fix the issue, if I use it with an `Event` it shows me `Type 'Event' is not assignable to type 'Event & { target: HTMLInputElement; }'.` too – Leonardo Rick Aug 22 '22 at 11:21
  • @LeonardoRick Most likely the Event types are incompatible. Probably the event you are trying to compare to is React Event (just guessing) and not a native browser Event. – smnbbrv Aug 30 '22 at 14:22
105

Here is the simple approach I used:

const element = event.currentTarget as HTMLInputElement
const value = element.value

The error shown by TypeScript compiler is gone and the code works.

Torsten Barthel
  • 3,059
  • 1
  • 26
  • 22
  • 2
    if you are using checkboxes too this is a good solution: const value = e.currentTarget.type === 'checkbox' ? (e.currentTarget as HTMLInputElement).checked : e.currentTarget.value – td18 Oct 21 '20 at 14:13
75

For those who are getting this error in angular 13, since upgrading from a previous version

The issue for me was I was using the following in my HTML (which was allowed before)

(keyup)="applyFilter($event.target.value)" 

The fix for this was:

HTML:

(keyup)="applyFilter($event)"

Component - in my called function:

applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    // ... etc...
}

And now I could use filterValue again as before.

James D
  • 1,975
  • 2
  • 17
  • 26
  • 5
    As Angular becomes heavier on typing this is the answer to this entire thread. All other answers are N/A. Send the `Event` work with typings, target and values in your controller. – Ben Racicot Jun 12 '22 at 16:46
  • superb, this is the needed answer – java-addict301 Aug 29 '22 at 16:23
  • I have `(change)="someVar = $event.target.value"` so I do not want to make another listener in my component. This is explicitly on a page element so it should be possible to infer the type of event. It works flawlessly like this, but I don't like having a type error. Any suggestions to solve it inline? – ThaJay Sep 23 '22 at 10:12
  • @ThaJay was there a reason you're using `(change)` instead of a one or two-way binding between the component and the HTML field? – James D Sep 28 '22 at 08:00
  • @JamesD I don't know, not that familiar with Angular yet. Would a binding be better in this case? – ThaJay Sep 29 '22 at 09:00
  • @ThaJay If changing one variable, just use a binding, if multiple things need to happen, use a function (like above). Sound like in your case you need ngModel, which can be one way or two way., ref https://angular.io/guide/binding-syntax#types-of-data-binding – James D Sep 29 '22 at 11:37
  • This is one way to solve the problem in Angular 13. If you don't want to change the component itself, you can also fix it in the HTML template: change `(keyup)="applyFilter($event.target.value)"` to `(keyup)="applyFilter($any($event.target).value)"` to disable type checking on this expression, then you can remove the error without having to deal with extracting the string from an HTMLInputElement in your function. – Colin Emonds Aug 02 '23 at 07:37
37

In my case, I had:

const handleOnChange = (e: ChangeEvent) => {
  doSomething(e.target.value);
}

And the issue was that I did not provide a type argument to ChangeEvent so that it knows e.target was an HTMLInputElement. Even if I manually told it that target was an input element (e.g. const target: HTMLInputElement = e.target), the ChangeEvent still didn't know that made sense.

The solution was to do:

// add type argument
const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
  doSomething(e.target.value);
}
Lewis
  • 4,285
  • 1
  • 23
  • 36
35

Since I reached two questions searching for my problem in a slightly different way, I am replicating my answer in case you end up here.

In the called function, you can define your type with:

emitWordCount(event: { target: HTMLInputElement }) {
  this.countUpdate.emit(event.target.value);
}

This assumes you are only interested in the target property, which is the most common case. If you need to access the other properties of event, a more comprehensive solution involves using the & type intersection operator:

event: Event & { target: HTMLInputElement }

You can also go more specific and instead of using HTMLInputElement you can use e.g. HTMLTextAreaElement for textareas.

belvederef
  • 2,195
  • 19
  • 16
35

Angular 10+

Open tsconfig.json and disable strictDomEventTypes.

  "angularCompilerOptions": {
    ....
    ........
    "strictDomEventTypes": false
  }
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
7guyo
  • 3,047
  • 1
  • 30
  • 31
  • 25
    Disabling strict rules can allow for improper code to be written, this isn't very good advice, especially with the advancements of Angular 10+. – cklimowski Apr 02 '21 at 22:53
  • 10
    Neither is having to cast type explicitly I am afraid, especially in such a small/local scope. It gives the appearance of some type safety, but is really only as good/bad as disabling the strict check. – Xinchao May 05 '21 at 08:44
  • 3
    This method works for me. Casting is not working in the html. This does not work `(keyup)="doFilter(($event.target as HTMLTextAreaElement).value)"` – Paul John Leonard Jul 15 '21 at 13:59
  • @cklimowski - for me this is the only way I could do in order to make the application running in angular old version in the latest version. – aj go Jul 03 '22 at 06:20
  • 1
    This is the correct answer. If angular doesn't know the type then it should keep it's mouth shut or at best restrict itself to showing a warning. Doing a cast just overrides the error in the same way but with a false sense of security. – m12lrpv Aug 15 '22 at 22:56
21

consider $any()

<textarea (keyup)="emitWordCount($any($event))"></textarea>
Ali Maher
  • 251
  • 2
  • 8
  • 1
    All this solution not apply in angular 10+,So please specify angular version. – Rahi.Shah Mar 03 '21 at 09:14
  • 6
    @Rahi.Shah I tried it on Angular 12 and it worked. If you are trying to access a nested property, you need to wrap the object around the propety, e.g. `$any($event.target).value`, this will mark the `event.target` property as `any` and you can access the value for example. – Shirohige May 03 '21 at 19:26
  • @Rahi.Shah I did use this in angular 12, but this solution https://stackoverflow.com/a/68106554/11135174 is way cleaner, since you still use the capabilities of Typescript without any "dirty fix". – Raphaël Balet Oct 07 '21 at 14:00
  • Works for me in angular 13. `$any($event.target).checked` – Franklin'j Gil'z Jul 13 '22 at 19:48
21

Don't use the workarounds from other answers that involve casting the event, the event target or entirely disabling type checking. It is not safe.

Instead you should "pluck" the value from the HTML element and pass that as an argument instead.

Oversimplified example:

<input #searchBox type="text" (input)="doSearch(searchBox.value)">
doSearch(text: string): void {
}

So if you expand that to the original example, you should get this:

import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'text-editor',
  template: `
    <textarea #text (keyup)="emitWordCount(text.value)"></textarea>
  `
})
export class TextEditorComponent {
  @Output() countUpdate = new EventEmitter<number>();

  emitWordCount(text: string) {
    this.countUpdate.emit(
      (text.match(/\S+/g) || []).length);
  }
}
Steven Liekens
  • 13,266
  • 8
  • 59
  • 85
  • Could you elaborate why this isn't "safe" ? – Raphaël Balet Feb 18 '22 at 10:28
  • 4
    @RaphaëlBalet because casting is effectively telling the TypeScript compiler "don't do strict type checking on this line of code". A cast object only has programmer type checking, and brains are unreliable. My proposed solution does not rely on casting or dynamic typing. – Steven Liekens Feb 18 '22 at 12:44
  • 2
    Excellent solution – atwright147 Apr 09 '22 at 11:11
  • 2
    This seems to be the now-recommended way from Angular: https://angular.io/guide/user-input#get-user-input-from-a-template-reference-variable – waternova Jul 19 '22 at 22:04
  • This sounds like a nice solution. But now I get the following type error on my number input: `Type 'string | number' is not assignable to type 'number'.` – ThaJay Sep 23 '22 at 10:19
  • 1
    @ThaJay it's not a type error, it can actually be a string and you need to write code to convert it to a number. Even better: use `numberInput.valueAsNumber`. – Steven Liekens Sep 26 '22 at 08:42
  • You are correct. I tested it and a number input can also have a string as value. My solution in the end was a simple `Number(inputElement.value)` but directly using `valueAsNumber` is even nicer. – ThaJay Sep 27 '22 at 10:39
13

Here is another simple approach, I used;

    inputChange(event: KeyboardEvent) {      
    const target = event.target as HTMLTextAreaElement;
    var activeInput = target.id;
    }
Burak Odabaş
  • 347
  • 2
  • 8
8
fromEvent<KeyboardEvent>(document.querySelector('#searcha') as HTMLInputElement , 'keyup')
    .pipe(
      debounceTime(500),
      distinctUntilChanged(),
      map(e  => {
            return e.target['value']; // <-- target does not exist on {}
        })
    ).subscribe(k => console.log(k));

Maybe something like the above could help. Change it based on the real code. The issue is ........ target['value']

Pang
  • 9,564
  • 146
  • 81
  • 122
Ahmed
  • 158
  • 1
  • 4
7

Angular 11+

Open tsconfig.json and disable strictTemplates.

 "angularCompilerOptions": {
    ....
    ........
    "strictTemplates": false
  }
ParisaN
  • 1,816
  • 2
  • 23
  • 55
Rahi.Shah
  • 1,305
  • 11
  • 20
4

Best way is to use templating => add id to your input and then use it value

<input type="text" #notaryLockup (keyup) = "searchNotary(notaryLockup.value)"placeholder="Entrez des information" >

searchNotary(value: string) {
 // your logic
}

this way you will never have Typescript error when strict verification is activated => See angular Docs

Idriss Sakhi
  • 267
  • 2
  • 5
4

Make a global.d.ts file in your src folder (or wherever you want)

add this code:

interface EventTarget {
  value: any
}

Enjoy.

And yes, this is exactly the kind of detail you should NOT be wasting your time on. ⌚⏲⏳

Bobby Connolly
  • 317
  • 2
  • 9
3

User TypeScript built in utility type Partial<Type>

In your template

(keyup)="emitWordCount($event.target)"

In your component

 emitWordCount(target: Partial<HTMLTextAreaElement>) {
    this.countUpdate.emit(target.value./*...*/);
  }
Monfa.red
  • 4,532
  • 1
  • 12
  • 14
  • Thx a lot, working like a charm. For Angular users, use this and do not forget to enable `"strictDomEventTypes": true`. Also working with `Partial` for inputs – Raphaël Balet Oct 07 '21 at 13:49
1

I believe it must work but any ways I'm not able to identify. Other approach can be,

<textarea (keyup)="emitWordCount(myModel)" [(ngModel)]="myModel"></textarea>


export class TextEditorComponent {
   @Output() countUpdate = new EventEmitter<number>();

   emitWordCount(model) {
       this.countUpdate.emit(
         (model.match(/\S+/g) || []).length);
       }
}
micronyks
  • 54,797
  • 15
  • 112
  • 146
1

Here is one more way to specify event.target:

import { Component, EventEmitter, Output } from '@angular/core';

@Component({
    selector: 'text-editor',
    template: `<textarea (keyup)="emitWordCount($event)"></textarea>`
})
export class TextEditorComponent {

   @Output() countUpdate = new EventEmitter<number>();

    emitWordCount({ target = {} as HTMLTextAreaElement }) { // <- right there

        this.countUpdate.emit(
          // using it directly without `event`
            (target.value.match(/\S+/g) || []).length);
    }
}
qiAlex
  • 4,290
  • 2
  • 19
  • 35
0

Use currentValue instead, as the type of currentValue is EventTarget & HTMLInputElement.

Charles Zhao
  • 158
  • 11
0

I faced a similar problem and this is how I resolved it. By using this custom generic type

export type DatasetInjector<T, D extends DOMStringMap> = T & {
  dataset: D;
};

How does it work?

In DatasetInjector, the generic T specifies the HTML typescript type, such as HTMLInputElement or HTMLTextAreaElement. The second generic D is used to provide missing information about data attributes of the input tag (or any other HTML tag).

Example

Here we are adding the data attribute inputId for HTMLInputElement

// Now this can be used as funciton type for onChange handler for input element
// ChangeEventHandler<DatasetInjector<HTMLInputElement, { inputId: string }>>

export type DatasetInjector<HTMLInputElement, D extends DOMStringMap> = HTMLInputElement & {
  dataset: D; // D -> { inputId: string }
};

For more info see my article here Reference link

just-be-weird
  • 1,242
  • 9
  • 7
0

$any($event.target ).value should do the trick.

Niranjan
  • 1,776
  • 1
  • 13
  • 21