¿Por qué Event.target no es un elemento en TypeScript?

115

Simplemente quiero hacer esto con mi KeyboardEvent

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

Si bien Event.target es de tipo EventTarget, no hereda de Element. Así que tengo que lanzarlo así:

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

Esto probablemente se deba a que algunos navegadores no siguen los estándares, ¿verdad? ¿Cuál es la implementación independiente del navegador correcta en TypeScript?

PD: Estoy usando jQuery para capturar KeyboardEvent.

daniel.sedlacek
fuente
7
Sintaxis un poco más limpiavar element = ev.target as HTMLElement
Adrian Moisa

Respuestas:

57

No hereda de Elementporque no todos los destinos de eventos son elementos.

De MDN :

El elemento, el documento y la ventana son los objetivos de eventos más comunes, pero otros objetos también pueden ser objetivos de eventos, por ejemplo, XMLHttpRequest, AudioNode, AudioContext y otros.

Incluso el KeyboardEventque está intentando usar puede ocurrir en un elemento DOM o en el objeto de la ventana (y teóricamente en otras cosas), por lo que no tendría sentido evt.targetdefinirlo como un Element.

Si es un evento en un elemento DOM, entonces diría que puede asumir con seguridad evt.target. es un Element. No creo que esto sea una cuestión de comportamiento entre navegadores. Simplemente esa EventTargetes una interfaz más abstracta que Element.

Más información: https://typescript.codeplex.com/discussions/432211

JLRishe
fuente
8
En ese caso, KeyboardEvent y MouseEvent deberían tener su propio equivalente de EventTarget que siempre contendrá el elemento asociado. DOM es tan poco fiable ...: /
daniel.sedlacek
7
No soy un experto en DOM ni TypeScript, pero diría que el diseño del EventTarget tiene demasiada ambigüedad y eso no tiene nada que ver con TypeScript.
daniel.sedlacek
2
@ daniel.sedlacek Por otro lado, KeyboardEvents puede ocurrir tanto en elementos DOM como en el objeto de la ventana (y teóricamente otras cosas), así que ahí mismo es imposible dar KeyboardEvent.targetun tipo que sea más específico que EventTarget, a menos que pienses KeyboardEventque también debería ser un tipo genérico KeyboardEvent<T extends EventTarget>y me gustaría verse obligado a poner KeyboardEvent<Element>todo en su código. En ese punto, es mejor hacer el yeso explícito, por doloroso que pueda ser.
JLRishe
14
En los casos en que sea útil para cualquier otra persona en el futuro, necesitaba lanzar como un tipo de elemento específico para acceder a la valuepropiedad de una <select>etiqueta. por ejemplolet target = <HTMLSelectElement> evt.target;
munsellj
1
@munsellj (desafortunadamente) esa es la forma correcta de manejar ambigüedades en un entorno escrito.
pilau
47

Usando mecanografiado, uso una interfaz personalizada que solo se aplica a mi función. Caso de uso de ejemplo.

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

En este caso, handleChange recibirá un objeto con un campo de destino que es de tipo HTMLInputElement.

Más adelante en mi código puedo usar

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

Un enfoque más limpio sería poner la interfaz en un archivo separado.

interface HandleNameChangeInterface {
  target: HTMLInputElement;
}

luego use la siguiente definición de función:

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

En mi caso de uso, se define expresamente que la única persona que llama para manejar el cambio es un tipo de elemento HTML de texto de entrada.

Bangonkali
fuente
Esto funcionó perfectamente para mí: estaba probando todo tipo de maldades, extendiendo EventTarget, etc. pero esta es la solución más limpia +1
Kitson
1
Solo para agregar a esto, si necesita extender la definición de evento, puede hacer algo como esto:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson
40

La respuesta de JLRishe es correcta, así que simplemente uso esto en mi controlador de eventos:

if (event.target instanceof Element) { /*...*/ }
Simon Epskamp
fuente
28

Texto mecanografiado 3.2.4

Para recuperar la propiedad, debe convertir el objetivo al tipo de datos apropiado:

e => console.log((e.target as Element).id)
dimpiax
fuente
¿Es lo mismo que la <HTMLInputElement>event.target;sintaxis?
Konrad Viltersten
@KonradViltersten, hacen lo mismo. La assintaxis se introdujo porque entraba en conflicto con JSX. Se recomienda usar aspara mantener la consistencia. basarat.gitbooks.io/typescript/docs/types/type-assertion.html
Adam
Ajá ya veo. También está apareciendo más C # 'ish, lo que en muchos casos es una ventaja, dependiendo de la experiencia de backend del equipo. Siempre que no sea uno de esos falsos amigos donde la sintaxis se parece a algo pero implica algo totalmente diferente técnicamente. (Estoy pensando en var y const entre Angular y C #, una triste experiencia mía, jeje).
Konrad Viltersten
2

¿Podría crear su propia interfaz genérica que se extienda Event. ¿Algo como esto?

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

Entonces puedes usarlo como:

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

Con mecanografiado podemos aprovechar los alias de tipo, así:

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...
};
Maksim Kostromin
fuente
0

@Bangonkali proporciona la respuesta correcta, pero esta sintaxis parece más legible y más agradable para mí:

eventChange($event: KeyboardEvent): void {
    (<HTMLInputElement>$event.target).value;
}
hovado
fuente