Dlaczego Event.target nie jest elementem w Typescript?


116

Po prostu chcę to zrobić za pomocą mojego KeyboardEvent

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

Chociaż Event.target jest typu EventTarget, nie dziedziczy po Element. Więc muszę to rzucić w ten sposób:

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

Jest to prawdopodobnie spowodowane tym, że niektóre przeglądarki nie przestrzegają standardów, prawda? Jaka jest poprawna implementacja agnostyczna przeglądarki w języku TypeScript?

PS: używam jQuery do przechwytywania zdarzenia KeyboardEvent.


7
Nieco czystsza składniavar element = ev.target as HTMLElement
Adrian Moisa,

Odpowiedzi:


57

Nie dziedziczy po, Elementponieważ nie wszystkie cele zdarzenia są elementami.

Z MDN :

Element, dokument i okno są najczęstszymi celami zdarzeń, ale inne obiekty mogą być również celami zdarzeń, na przykład XMLHttpRequest, AudioNode, AudioContext i inne.

Nawet ten KeyboardEvent, którego próbujesz użyć, może wystąpić na elemencie DOM lub obiekcie okna (i teoretycznie na innych rzeczach), więc tutaj nie miałoby sensu evt.targetdefiniowanie go jako Element.

Jeśli jest to zdarzenie na elemencie DOM, to powiedziałbym, że można spokojnie założyć evt.target. jest Element. Nie sądzę, że jest to kwestia zachowania różnych przeglądarek. Po prostu EventTargetjest to bardziej abstrakcyjny interfejs niż Element.

Więcej informacji: https://typescript.codeplex.com/discussions/432211


8
W takim przypadku KeyboardEvent i MouseEvent powinny mieć swój własny odpowiednik EventTarget, który zawsze będzie zawierał powiązany element. DOM jest taki podejrzany ...: /
daniel.sedlacek

7
Nie jestem ekspertem od DOM ani TypeScript, ale powiedziałbym, że projekt EventTarget jest zbyt niejednoznaczny i nie ma to nic wspólnego z TypeScript.
daniel.sedlacek

2
@ daniel.sedlacek Z drugiej strony, KeyboardEvents może wystąpić zarówno na elementach DOM, jak i na obiekcie okna (i teoretycznie na innych rzeczach), więc tutaj nie można podać KeyboardEvent.targettypu bardziej szczegółowego niż EventTarget, chyba że myślisz, że KeyboardEventpowinien to być generic type KeyboardEvent<T extends EventTarget>i chciałby być zmuszony do umieszczania KeyboardEvent<Element>wszystkiego w kodzie. W tym momencie lepiej jest po prostu wykonać wyraźną obsadę, choć może to być bolesne.
JLRishe

14
Jeśli w przyszłości będzie to pomocne dla kogokolwiek innego, musiałem rzutować jako określony typ elementu, aby uzyskać dostęp do valuewłaściwości <select>tagu. np.let target = <HTMLSelectElement> evt.target;
munsellj

1
@munsellj (niestety), to poprawny sposób radzenia sobie z niejednoznacznościami w środowisku wpisywanym.
pilau

47

Używając maszynopisu, używam niestandardowego interfejsu, który ma zastosowanie tylko do mojej funkcji. Przykład użycia.

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

W tym przypadku handleChange otrzyma obiekt z polem docelowym typu HTMLInputElement.

Później w moim kodzie mogę użyć

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

Czystszym podejściem byłoby umieszczenie interfejsu w osobnym pliku.

interface HandleNameChangeInterface {
  target: HTMLInputElement;
}

następnie użyj następującej definicji funkcji:

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

W moim przypadku jest wyraźnie zdefiniowane, że jedynym elementem wywołującym do handleChange jest typ elementu HTML wejściowego tekstu.


To zadziałało idealnie dla mnie - próbowałem wszelkiego rodzaju złośliwości, rozszerzałem EventTarget itp., Ale to jest najczystsze rozwiązanie +1
Kitson

1
Aby to dodać, jeśli chcesz rozszerzyć definicję wydarzenia, możesz zrobić coś takiego:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson

41

Odpowiedź JLRishe jest poprawna, więc po prostu używam tego w moim module obsługi zdarzeń:

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

28

Maszynopis 3.2.4

Aby pobrać właściwość, należy rzutować cel na odpowiedni typ danych:

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

Czy to to samo, co <HTMLInputElement>event.target;składnia?
Konrad Viltersten

@KonradViltersten, robią to samo. asSkładnia została wprowadzona, ponieważ kolidowały z JSX. Zaleca się używać asdo konsystencji. basarat.gitbooks.io/typescript/docs/types/type-assertion.html
Adam

Aha widzę. Wydaje się również, że jest bardziej C #, co w wielu przypadkach jest zaletą, w zależności od doświadczenia zespołu w zapleczu. O ile nie jest to jeden z tych fałszywych przyjaciół, których składnia przypomina coś, ale implikuje coś zupełnie innego technicznie. (Myślę o var i const między Angular i C #, moje smutne doświadczenie, hehe).
Konrad Viltersten

2

Czy mógłbyś stworzyć własny interfejs, który rozszerza Event. Coś takiego?

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

Następnie możesz go używać tak:

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

1

Za pomocą maszynopisu możemy wykorzystać aliasy typów, takie jak:

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 udzieli poprawnej odpowiedzi, ale ta składnia wydaje mi się bardziej czytelna i po prostu ładniejsza:

eventChange($event: KeyboardEvent): void {
    (<HTMLInputElement>$event.target).value;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.