Por que Event.target não é Element em Typescript?


115

Eu simplesmente quero fazer isso com meu KeyboardEvent

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

Embora Event.target seja do tipo EventTarget, ele não herda de Element. Então eu tenho que lançar assim:

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

Isso provavelmente se deve a alguns navegadores que não seguem os padrões, certo? Qual é a implementação independente de navegador correta no TypeScript?

PS: Estou usando o jQuery para capturar o KeyboardEvent.


7
Sintaxe um pouco mais limpavar element = ev.target as HTMLElement
Adrian Moisa

Respostas:


57

Não herda de Elementporque nem todos os destinos de evento são elementos.

Do MDN :

Elemento, documento e janela são os alvos de eventos mais comuns, mas outros objetos também podem ser alvos de eventos, por exemplo XMLHttpRequest, AudioNode, AudioContext e outros.

Até mesmo o que KeyboardEventvocê está tentando usar pode ocorrer em um elemento DOM ou no objeto de janela (e teoricamente em outras coisas), portanto, não faria sentido evt.targetser definido como um Element.

Se for um evento em um elemento DOM, eu diria que você pode assumir com segurança evt.target. é um Element. Não acho que seja uma questão de comportamento entre navegadores. Simplesmente essa EventTargeté uma interface mais abstrata do que Element.

Leitura adicional: https://typescript.codeplex.com/discussions/432211


8
Nesse caso, KeyboardEvent e MouseEvent devem ter seu próprio equivalente de EventTarget que sempre conterá o Elemento associado. DOM é tão duvidoso ...: /
daniel.sedlacek

7
Não sou um especialista em DOM nem TypeScript, mas diria que o design do EventTarget tem muita ambigüidade e isso não tem nada a ver com o TypeScript.
daniel.sedlacek

2
@ daniel.sedlacek Por outro lado, KeyboardEvents podem ocorrer em elementos DOM e no objeto janela (e, teoricamente, outras coisas), então é impossível fornecer KeyboardEvent.targetum tipo que seja mais específico do que EventTarget, a menos que você pense KeyboardEventque também deve ser um tipo genérico KeyboardEvent<T extends EventTarget>e gostaria de ser forçado a colocar KeyboardEvent<Element>tudo em todo o código. Nesse ponto, é melhor você apenas fazer o gesso explícito, por mais doloroso que seja.
JLRishe

14
Caso seja útil para qualquer outra pessoa no futuro, eu precisava fazer o cast como um tipo de elemento específico para acessar a valuepropriedade de uma <select>tag. por exemplolet target = <HTMLSelectElement> evt.target;
munsellj

1
@munsellj (infelizmente) essa é a maneira correta de lidar com ambigüidades em um ambiente digitado.
pilau de

47

Usando o typescript, eu uso uma interface personalizada que se aplica apenas à minha função. Exemplo de caso de uso.

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

Nesse caso, o handleChange receberá um objeto com campo de destino do tipo HTMLInputElement.

Posteriormente em meu código, posso usar

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

Uma abordagem mais limpa seria colocar a interface em um arquivo separado.

interface HandleNameChangeInterface {
  target: HTMLInputElement;
}

em seguida, use a seguinte definição de função:

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

No meu caso de uso, está expressamente definido que o único chamador para handleChange é um tipo de elemento HTML de texto de entrada.


Isso funcionou perfeitamente para mim - eu estava tentando todo tipo de maldade, estendendo EventTarget etc., mas esta é a solução mais limpa 1
Kitson

1
Só para adicionar a isso, se você precisar estender a definição de evento, você pode fazer algo assim:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson

40

A resposta de JLRishe está correta, então eu simplesmente uso isso em meu manipulador de eventos:

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

28

Texto datilografado 3.2.4

Para recuperar a propriedade, você deve converter o destino para o tipo de dados apropriado:

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

É a mesma <HTMLInputElement>event.target;sintaxe?
Konrad Viltersten

@KonradViltersten, eles fazem a mesma coisa. A assintaxe foi introduzida porque entrava em conflito com JSX. É recomendado usar aspara consistência. basarat.gitbooks.io/typescript/docs/types/type-assertion.html
Adam

Aha eu vejo. Também está parecendo mais C # ', o que em muitos casos é uma vantagem, dependendo da experiência de back-end da equipe. Contanto que não seja um daqueles falsos amigos onde a sintaxe se assemelha a algo, mas implica algo totalmente diferente tecnicamente. (Estou pensando em var e const entre Angular e C #, uma experiência triste para mim, hehe).
Konrad Viltersten

2

Você poderia criar sua própria interface genérica que se estende Event. Algo assim?

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

Então você pode usá-lo como:

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

1

Com o texto digitado, podemos aproveitar aliases de tipo, como:

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 fornece a resposta certa, mas essa sintaxe parece mais legível e simplesmente mais agradável para mim:

eventChange($event: KeyboardEvent): void {
    (<HTMLInputElement>$event.target).value;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.