Почему Event.target не является элементом в Typescript?


117

Я просто хочу сделать это с помощью KeyboardEvent

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

Хотя Event.target имеет тип EventTarget, он не наследуется от Element. Поэтому я должен использовать это так:

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

Вероятно, это связано с тем, что некоторые браузеры не соответствуют стандартам, верно? Какая правильная реализация в TypeScript является независимой от браузера?

PS: я использую jQuery для захвата KeyboardEvent.


7
Немного более чистый синтаксисvar element = ev.target as HTMLElement
Адриан Мойса 07

Ответы:


57

Он не наследуется от, Elementпотому что не все цели событий являются элементами.

Из MDN :

Элемент, документ и окно являются наиболее распространенными целями событий, но целями событий могут быть и другие объекты, например XMLHttpRequest, AudioNode, AudioContext и другие.

Даже то, KeyboardEventчто вы пытаетесь использовать, может встречаться в элементе DOM или в объекте окна (и теоретически в других вещах), поэтому прямо здесь не имеет смысла evt.targetбыть определенным как Element.

Если это событие в элементе DOM, я бы сказал, что вы можете смело предположить evt.target. это Element. Я не думаю, что дело в кроссбраузерности. Просто EventTargetэто более абстрактный интерфейс, чем Element.

Дополнительная литература: https://typescript.codeplex.com/discussions/432211


8
В этом случае KeyboardEvent и MouseEvent должны иметь собственный эквивалент EventTarget, который всегда будет содержать связанный элемент. DOM такой изворотливый ...: /
daniel.sedlacek 06

7
Я не эксперт ни в DOM, ни в TypeScript, но я бы сказал, что дизайн EventTarget имеет слишком много двусмысленности и не имеет ничего общего с TypeScript.
daniel.sedlacek 06

2
@ daniel.sedlacek С другой стороны, KeyboardEvents может встречаться как в элементах DOM, так и в объекте окна (и теоретически в других вещах), поэтому прямо здесь невозможно указать KeyboardEvent.targetтип, который был бы более конкретным, чем EventTarget, если вы не думаете, что он KeyboardEventтакже должен быть универсальный тип, KeyboardEvent<T extends EventTarget>и хотелось бы, чтобы его заставляли использовать KeyboardEvent<Element>весь код. В этот момент вам лучше просто выполнить явное приведение, хотя это может быть болезненно.
JLRishe

14
В случаях, когда это будет полезно для кого-то еще в будущем, мне нужно было привести к конкретному типу элемента, чтобы получить доступ к valueсвойству <select>тега. eglet target = <HTMLSelectElement> evt.target;
munsellj

1
@munsellj (к сожалению), это правильный способ справиться с двусмысленностями в типизированной среде.
плов

47

Используя машинописный текст, я использую настраиваемый интерфейс, который применим только к моей функции. Пример использования.

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

В этом случае handleChange получит объект с целевым полем типа HTMLInputElement.

Позже в моем коде я могу использовать

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

Более чистый подход - поместить интерфейс в отдельный файл.

interface HandleNameChangeInterface {
  target: HTMLInputElement;
}

затем позже используйте следующее определение функции:

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

В моем случае использования явно определено, что единственный вызывающий объект handleChange - это тип входного текста HTML-элемента.


Это отлично сработало для меня - я пробовал всевозможные гадости, расширял EventTarget и т. Д., Но это самое чистое решение +1
Китсон

1
Чтобы добавить к этому, если вам нужно расширить определение события, вы можете сделать что-то вроде этого:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson

41

Ответ JLRishe правильный, поэтому я просто использую его в своем обработчике событий:

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

28

Машинопись 3.2.4

Для получения свойства вы должны привести target к соответствующему типу данных:

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

Это то же самое, что и <HTMLInputElement>event.target;синтаксис?
Конрад Вильтерстен

@KonradViltersten, они делают то же самое. asСинтаксис был введен , поскольку он противоречил JSX. Рекомендуется использовать asдля постоянства. basarat.gitbooks.io/typescript/docs/types/type-assertion.html
Адам

Ага, понятно. Также появляется все больше и больше C #, что во многих случаях является преимуществом, в зависимости от опыта команды. Если только это не один из тех ложных друзей, синтаксис которых на что-то похож, а подразумевает нечто совершенно иное технически. (Я думаю, что var и const между Angular и C #, мой печальный опыт, хе-хе).
Конрад Вильтерстен

2

Не могли бы вы создать свой собственный общий интерфейс, который расширяется Event. Что-то вроде этого?

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

Тогда вы можете использовать это как:

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

1

С машинописным текстом мы можем использовать псевдонимы типов, например:

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 дает правильный ответ, но этот синтаксис кажется мне более читаемым и просто более приятным для меня:

eventChange($event: KeyboardEvent): void {
    (<HTMLInputElement>$event.target).value;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.