Perché Event.target non è Element in Typescript?


115

Voglio semplicemente farlo con il mio KeyboardEvent

var tag = evt.target.tagName.toLowerCase();

Sebbene Event.target sia di tipo EventTarget, non eredita da Element. Quindi devo lanciarlo in questo modo:

var tag = (<Element>evt.target).tagName.toLowerCase();

Ciò è probabilmente dovuto ad alcuni browser che non seguono gli standard, giusto? Qual è la corretta implementazione indipendente dal browser in TypeScript?

PS: sto usando jQuery per catturare il KeyboardEvent.


7
Sintassi un po 'più pulitavar element = ev.target as HTMLElement
Adrian Moisa

Risposte:


57

Non eredita da Elementperché non tutti i target di evento sono elementi.

Da MDN :

Elemento, documento e finestra sono i target di evento più comuni, ma anche altri oggetti possono essere target di evento, ad esempio XMLHttpRequest, AudioNode, AudioContext e altri.

Anche quello KeyboardEventche stai cercando di utilizzare può verificarsi su un elemento DOM o sull'oggetto finestra (e teoricamente su altre cose), quindi proprio lì non avrebbe senso evt.targetessere definito come file Element.

Se si tratta di un evento su un elemento DOM, allora direi che puoi tranquillamente presumere evt.target. è un Element. Non credo che sia una questione di comportamento cross-browser. Semplicemente questa EventTargetè un'interfaccia più astratta di Element.

Ulteriori letture: https://typescript.codeplex.com/discussions/432211


8
In questo caso KeyboardEvent e MouseEvent dovrebbero avere il proprio equivalente di EventTarget che conterrà sempre l'elemento associato. DOM è così losco ...: /
daniel.sedlacek

7
Non sono un esperto di DOM né di TypeScript ma direi che il design di EventTarget ha troppa ambiguità e questo non ha nulla a che fare con TypeScript.
daniel.sedlacek

2
@ daniel.sedlacek D'altra parte, KeyboardEvents possono verificarsi su entrambi gli elementi DOM e sull'oggetto finestra (e teoricamente altre cose), quindi proprio lì è impossibile dare KeyboardEvent.targetun tipo che sia più specifico di EventTarget, a meno che non si pensi KeyboardEventdovrebbe essere anche un tipo generico KeyboardEvent<T extends EventTarget>e vorrei essere costretto a mettere KeyboardEvent<Element>tutto il tuo codice. A quel punto, faresti meglio a fare il cast esplicito, per quanto doloroso possa essere.
JLRishe

14
Nei casi in cui è utile per chiunque altro in futuro, avevo bisogno di eseguire il cast come un tipo di elemento specifico per accedere alla valueproprietà di un <select>tag. ad esempiolet target = <HTMLSelectElement> evt.target;
munsellj

1
@munsellj (sfortunatamente) questo è il modo corretto per gestire le ambiguità in un ambiente tipizzato.
pilau

47

Utilizzando il dattiloscritto, utilizzo un'interfaccia personalizzata che si applica solo alla mia funzione. Caso d'uso di esempio.

  handleChange(event: { target: HTMLInputElement; }) {
    this.setState({ value: event.target.value });
  }

In questo caso, handleChange riceverà un oggetto con un campo di destinazione di tipo HTMLInputElement.

Più avanti nel mio codice posso usare

<input type='text' value={this.state.value} onChange={this.handleChange} />

Un approccio più pulito sarebbe mettere l'interfaccia in un file separato.

interface HandleNameChangeInterface {
  target: HTMLInputElement;
}

quindi in seguito utilizzare la seguente definizione di funzione:

  handleChange(event: HandleNameChangeInterface) {
    this.setState({ value: event.target.value });
  }

Nel mio caso d'uso, è espressamente definito che l'unico chiamante per handleChange è un tipo di elemento HTML di testo di input.


Questo ha funzionato perfettamente per me: stavo provando ogni sorta di cattiveria, estendendo EventTarget ecc. Ma questa è la soluzione più pulita +1
Kitson

1
Solo per aggiungere a questo, se hai bisogno di estendere la definizione dell'evento puoi fare qualcosa del genere:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson

40

La risposta di JLRishe è corretta, quindi lo uso semplicemente nel mio gestore di eventi:

if (event.target instanceof Element) { /*...*/ }

28

Dattiloscritto 3.2.4

Per recuperare la proprietà è necessario eseguire il cast del target sul tipo di dati appropriato:

e => console.log((e.target as Element).id)

È la stessa della <HTMLInputElement>event.target;sintassi?
Konrad Viltersten

@KonradViltersten, fanno la stessa cosa. La assintassi è stata introdotta perché era in conflitto con JSX. Si consiglia di utilizzare asper coerenza. basarat.gitbooks.io/typescript/docs/types/type-assertion.html
Adam,

Aha, capisco. Appare anche più C # 'ish che in molti casi è un vantaggio, a seconda dell'esperienza di back-end del team. Finché non è uno di quei falsi amici in cui la sintassi assomiglia a qualcosa ma implica qualcosa di totalmente diverso tecnicamente. (Sto pensando var e const tra Angular e C #, una mia triste esperienza, hehe).
Konrad Viltersten

2

Potresti creare la tua interfaccia generica che si estende Event. Qualcosa come questo?

interface DOMEvent<T extends EventTarget> extends Event {
  target: T
}

Quindi puoi usarlo come:

handleChange(event: DOMEvent<HTMLInputElement>) {
  this.setState({ value: event.target.value });
}

1

Con il dattiloscritto possiamo sfruttare gli alias di tipo, in questo modo:

type KeyboardEvent = {
  target: HTMLInputElement,
  key: string,
};
const onKeyPress = (e: KeyboardEvent) => {
  if ('Enter' === e.key) { // Enter keyboard was pressed!
    submit(e.target.value);
    e.target.value = '';
    return;
  }
  // continue handle onKeyPress input events...
};

0

@Bangonkali fornisce la risposta giusta, ma questa sintassi mi sembra più leggibile e semplicemente più carina:

eventChange($event: KeyboardEvent): void {
    (<HTMLInputElement>$event.target).value;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.