How to fix property not existing on EventTarget in TypeScript
A common error that occurs when working with the DOM in TypeScript
is “property ‘property name’ not existing on EventTarget
”. This error occurs
when you try to access a property on an event target in TypeScript.
Property 'value' does not exist on type 'EventTarget'.
Here’s an example of a code snippet that triggers this error:
If you compile the above snippet, the compiler will emit two errors:
target
is possiblynull
.- Property
value
does not exist on typeEventTarget
.
The first error occurs because the type of the target
object is EventTarget | null
and we’re trying to access a property on a nullable type which is not
allowed in TypeScript (if strictNullChecks
is enabled). We can fix this error
by narrowing the type to just EventTarget
through a type guard:
function handleClick(event: Event) {
const { target } = event
if (target) console.log(target.value);
}
The second error occurs because the type of the target
object is now
EventTarget
, and this type only has three methods: addEventListener()
,
removeEventListener()
, and dispatchEvent()
. When you try to access any other
property on the target
object, it will throw an error because the property
will not be recognised in the EventTarget
type.
But we know that the target
element is a button in this case so we need to
communicate that information to TypeScript so that it can allow us access
properties that are valid for HTMLButtonElement
which is the appropriate type
for button elements in TypeScript.
The EventTarget
type does not inherit from HTMLElement
by default because
HTML elements are not the only things that can be event targets.
It’s left to you to determine what the proper type of the target
object is
before TypeScript can allow you to access any properties not found on
EventTarget
.
One way to fix this error is to type cast the target
object with the as
keyword.
function handleClick(event: Event) {
const { target } = event
if (target) console.log((target as HTMLButtonElement).value);
}
Or you can cast the type of event.target
to HTMLButtonElement
when creating
the target
variable:
function handleClick(event: Event) {
const target = event.target as HTMLButtonElement;
if (target) console.log(target.value);
}
Either way, the code compiles because you’ve asked the compiler to treat the
target
object as an HTMLButtonElement
so it allows you access the value
property since it exists on the type.
Another way is to define the type of the target
object in the callback
function using the &
type intersection operator:
function handleClick(event: Event & {
target: HTMLButtonElement
}) {
const { target } = event
console.log(target.value);
}
This means we don’t need to use the type guard because the type of target
does
not include null
, and the types of the other properties on the event
object
are left unchanged.
If you need to do this often, you can abstract this pattern into its own type by
extending the HTMLElement
type with generics:
type HTMLElementEvent<T extends HTMLElement> = Event & {
target: T;
}
function handleClick(event: HTMLElementEvent<HTMLButtonElement>) {
const { target } = event
console.log(target.value);
}
Now, if you need to access an event target in a different context, all you need
to do is replace the HTMLButtonElement
type with the appropriate type for the
element.
Thanks for reading, and happy coding!