เหตุใด Event.target จึงไม่ใช่ Element ใน typescript


116

ฉันแค่ต้องการทำสิ่งนี้กับ 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
Adrian Moisa

คำตอบ:


57

มันไม่ได้รับมรดกจาก Elementเนื่องจากเป้าหมายเหตุการณ์ไม่ใช่องค์ประกอบทั้งหมด

จาก MDN :

องค์ประกอบเอกสารและหน้าต่างเป็นเป้าหมายเหตุการณ์ที่พบบ่อยที่สุด แต่วัตถุอื่น ๆ ก็สามารถเป็นเป้าหมายเหตุการณ์ได้เช่นกันตัวอย่างเช่น XMLHttpRequest, AudioNode, AudioContext และอื่น ๆ

แม้แต่สิ่งที่KeyboardEventคุณพยายามใช้ก็สามารถเกิดขึ้นได้ในองค์ประกอบ DOM หรือบนวัตถุหน้าต่าง (และในทางทฤษฎีกับสิ่งอื่น ๆ ) ดังนั้นจึงไม่สมเหตุสมผลevt.targetที่จะกำหนดเป็นElementไฟล์.

ถ้ามันเป็นเหตุการณ์ในองค์ประกอบ DOM evt.targetแล้วฉันจะบอกว่าคุณได้อย่างปลอดภัยสามารถสันนิษฐานได้ว่า คือElement. ฉันไม่คิดว่านี่เป็นเรื่องของพฤติกรรมข้ามเบราว์เซอร์ เพียงแค่ว่าเป็นอินเตอร์เฟซที่เป็นนามธรรมมากกว่าEventTargetElement

อ่านเพิ่มเติม: https://typescript.codeplex.com/discussions/432211


8
ในกรณีนั้น KeyboardEvent และ MouseEvent ควรมีเทียบเท่ากับ EventTarget ซึ่งจะมีองค์ประกอบที่เกี่ยวข้องเสมอ DOM หลบไป ... : /
daniel.sedlacek

7
ฉันไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับ DOM หรือ TypeScript แต่ฉันจะบอกว่าการออกแบบ EventTarget มีความคลุมเครือมากเกินไปและไม่มีส่วนเกี่ยวข้องกับ TypeScript
daniel.sedlacek

2
@ daniel.sedlacek ในทางกลับกันKeyboardEvents สามารถเกิดขึ้นได้ทั้งในองค์ประกอบ DOM และบนวัตถุหน้าต่าง (และในทางทฤษฎีอื่น ๆ ) ดังนั้นจึงเป็นไปไม่ได้ที่จะKeyboardEvent.targetระบุประเภทที่เฉพาะเจาะจงมากไปกว่าEventTargetนั้นเว้นแต่คุณคิดว่าKeyboardEventควรจะเป็น ประเภททั่วไปKeyboardEvent<T extends EventTarget>และต้องการบังคับให้ใส่KeyboardEvent<Element>รหัสทั้งหมดของคุณ เมื่อถึงจุดนั้นคุณจะดีกว่าเพียงแค่ทำการร่ายอย่างโจ่งแจ้งแม้ว่ามันอาจจะเจ็บปวดก็ตาม
JLRishe

14
ในกรณีที่เป็นประโยชน์สำหรับคนอื่น ๆ ในอนาคตฉันจำเป็นต้องแคสต์เป็นประเภทองค์ประกอบเฉพาะเพื่อเข้าถึงvalueคุณสมบัติของ<select>แท็ก เช่นlet target = <HTMLSelectElement> evt.target;
munsellj

1
@munsellj (น่าเสียดาย) นั่นเป็นวิธีที่ถูกต้องในการจัดการกับความคลุมเครือในสภาพแวดล้อมที่พิมพ์ผิด
pilau

47

การใช้ typescript ฉันใช้อินเทอร์เฟซแบบกำหนดเองที่ใช้กับฟังก์ชันของฉันเท่านั้น ตัวอย่างการใช้งาน

  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 });
  }

ใน usecase ของฉันมีการกำหนดไว้อย่างชัดเจนว่าผู้โทรเท่านั้นที่จะ handleChange คือประเภทขององค์ประกอบ HTML ของข้อความอินพุต


สิ่งนี้ใช้ได้ผลอย่างสมบูรณ์แบบสำหรับฉัน - ฉันพยายามทุกอย่างที่น่ารังเกียจขยาย EventTarget ฯลฯ แต่นี่เป็นทางออกที่สะอาดที่สุด +1
Kitson

1
เพื่อเพิ่มสิ่งนี้หากคุณต้องการขยายคำจำกัดความของเหตุการณ์คุณสามารถทำสิ่งนี้ได้:handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) => {...}
Kitson

41

คำตอบของ JLRishe ถูกต้องดังนั้นฉันจึงใช้สิ่งนี้ในตัวจัดการเหตุการณ์ของฉัน:

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

28

typescript 3.2.4

สำหรับการดึงคุณสมบัติคุณต้องส่งเป้าหมายไปยังประเภทข้อมูลที่เหมาะสม:

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

เหมือนกับ<HTMLInputElement>event.target;ไวยากรณ์หรือไม่
Konrad Viltersten

@KonradViltersten พวกเขาทำสิ่งเดียวกัน มีการนำasไวยากรณ์มาใช้เนื่องจากขัดแย้งกับ JSX ขอแนะนำให้ใช้asเพื่อความสม่ำเสมอ basarat.gitbooks.io/typescript/docs/types/type-assertion.html
อดัม

ฉันเห็น นอกจากนี้ยังมี C # 'มากขึ้นซึ่งในหลาย ๆ กรณีเป็นข้อดีขึ้นอยู่กับประสบการณ์แบ็กเอนด์ของทีม ตราบใดที่ไม่ใช่หนึ่งในเพื่อนจอมปลอมที่วากยสัมพันธ์มีลักษณะคล้ายกับอะไรบางอย่าง แต่มีนัยที่แตกต่างกันโดยสิ้นเชิงในทางเทคนิค (ฉันกำลังคิดvarและconstระหว่าง Angular และ C # ซึ่งเป็นประสบการณ์ที่น่าเศร้าของฉันฮิฮิ)
Konrad Viltersten

2

คุณสามารถสร้างอินเทอร์เฟซทั่วไปของคุณเองที่ขยายEventได้ อะไรทำนองนี้?

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

จากนั้นคุณสามารถใช้งานได้เช่น:

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

1

ด้วย typescript เราสามารถใช้ประโยชน์จากนามแฝงประเภทดังนี้:

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.