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:

index.ts
function handleClick(event: Event) {
  const { target } = event
  console.log(target.value);
}

const button = document.querySelector('button');
if (button) {
  button.addEventListener('click', handleClick);
}

Inspect this code on the TypeScript Playground

If you compile the above snippet, the compiler will emit two errors:

  1. target is possibly null.
  2. Property value does not exist on type EventTarget.
TypeScript compiler error message

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.

VS Code showing autocompletion options for EventTarget type

VS Code provides autocompletion for valid properties on EventTarget

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!