Home > Software engineering >  Getting ID of clicked element using strict type
Getting ID of clicked element using strict type

Time:03-19

Let us assume we want to log the ID attribute of a clicked element with the following TypeScript code:

onClick(event) {
  console.log(event.target.attributes.id.nodeValue);
}

The above function receives the clicked object as a parameter, but the type in this case is any. If I check the result of typeof event I get object. So I would modify my code as follows:

onClick(event: object) {
  console.log(event.target.attributes.id.nodeValue);
}

In this case, I receive an error in VS Code as below:

Property 'target' does not exist on type 'object'.ts(2339)

So what is up with this? Using the type any as parameter type works, the console logs the desired ID without any error, but if I decide to use a strict object as parameter type, my code does not compile. Where is the issue?

The affected HTML code is the following;

<h1 id="foo" (click)="onClick($event)">...</h1>

CodePudding user response:

If I check the result of typeof event I get object.

That's runtime code, so it's the JavaScript typeof operator, which says "object" for all objects (and null).

So I would modify my code as follows...I receive an error is VS Code like below

Plain objects don't have a target property.

The type of the event object for a click event is MouseEvent. (You've tagged and . If you're using jQuery or the DOM to attach the event, it'll be the DOM's MouseEvent. If you're using Angular to attach the event, it looks like it'll also be the DOM's MouseEvent. Caveat: I don't use Angular.) You'd also probably want event.currentTarget.id or event.target.id rather than using the attributes collection and nodeValue, since the id attribute is a reflected property.

But, the DOM's MouseEvent interface inherits its currentTarget property from Event, which just defines it as an EventTarget. You know that it will be an Element, but unfortunately the interface just says it's EventTarget. (The same is true for target.)

As far as I'm aware, there's no predefined interface for a MouseEvent that defines currentTarget as Element instead. You can reliably (in this situation) use a type assertion:

onClick(event: MouseEvent) {
    console.log((event.target as Element).id); // Or `(event.currentTarget as Element).id`
}

I don't like type assertions, but they are sometimes necessary.

Sometimes you see people doing that same assertion slightly differently, like this:

onClick({target}: {target: Element}) {
    console.log(target.id);
}
// or
onClick({currentTarget}: {currentTarget: Element}) {
    console.log(currentTarget.id);
}

You might create a reusable type instead, like this:

interface ElementMouseEvent extends MouseEvent {
    currentTarget: Element;
    target: Element;
}

...and then use it (which is still making an assertion, but again, a safe one):

onClick(event: ElementMouseEvent) {
    console.log(event.target.id); // Or `event.currentTarget.id`
}
  • Related