javascript - Détecter si un clic se trouvait à l'intérieur d'un composant React ou non en TypeScript


J'ai approximativement ce qui suit :

componentDidMount() {                                                                                                                                                                                           
  document.querySelector('body')!.addEventListener('click', this.click);
} 

click = (e: Event) => {                                                                                                                                                                                         
  if (this.state.toggled) {
    if (!ReactDom.findDOMNode(this.someRef).contains(e.target)) {
      this.setState({ toggled: false });
    }
  }
};

render() {
  return (<CustomElement
    ref={(e) => { this.someRef = e; }}                                                                                                                                                                   
  />)
}

Ce code détecte correctement si l'utilisateur clique à l'intérieur ou à l'extérieur du CustomElement, jusque-là tout va bien.

Cependant, tsc n’est pas du tout content de cela :

error TS2345: Argument of type 'EventTarget' is not assignable to parameter of type 'Node'.
  Property 'attributes' is missing in type 'EventTarget'.

En regardant node_modules/typescript/lib/lib.d.ts, cela a du sens, puisque e.target est un EventTarget qui semble seulement définir les fonctions pour ajouter et supprimer des gestionnaires d'événements. Cependant, MDN indique que e.target est « Une référence à l'objet qui a déclenché l'événement. », ce qui semble plus proche de ce que je veux.

Alors, comment puis-je conserver la fonctionnalité actuellement en fonctionnement, tout en satisfaisant tsc (au lieu de simplement faire taire l’erreur) ?

Réponse #1

J'ai trouvé une discussion similaire dans la file de problèmes de TypeScript qui dit que dans ce cas, une assertion de type est probablement inévitable :

Fondamentalement, EventTarget est le type le plus général, dont Element est un sous-type, et HTMLElement est un sous-type de celui-ci. Si vous récupérez un élément du DOM, nous n’avons généralement aucune idée de ce que c’est, et vous devriez ajouter une assertion de type pour « ajouter » la connaissance spécifique que vous avez de la structure de votre mise en page DOM particulière.

Il est possible, par exemple, que la .target d’un événement ne soit pas un Élément (vous pouvez ajouter des écouteurs d’événements à XMLHttpRequest, par exemple, et XMLHttpRequest n’a pas de méthode getBoundingClientRect).

Donc, comme la cible peut être ou ne pas être un Node comme je le veux, TS ne peut pas en déduire la vérité, et je dois l'affirmer.

contains(e.target en tant que Node) semble être la bonne solution.

Réponse #2

Que diriez-vous de définir votre gestionnaire de clic comme ceci :

private click = (e: Event) =>
{
    if (e.target instanceof HTMLElement && !ReactDOM.findDOMNode(this.someRef).contains(e.target))
    {
        if (this.state.toggled)
        {
            this.setState({ toggled: false });
        }
    }
}

Stylo d'exemple.