【Typescript】探究爲什麼TS對EventTarget類型判斷的比較大?

前言

  • 一般寫addEventListener,我們的listener都會收到一個事件對象,而這個事件對象的類型TS卻認爲是Event類型,而其target判斷爲EventTarget類型,給的類型比較大,往往都需要我們進行斷言,爲什麼不好判斷?難道真的有意料之外的結果?
  • 特別是一些我們感覺很確定的東西,比如MouseEvent,是click後的事件,不管屏幕上怎麼點擊,點的怎麼看都是個HTMLElement,難道還會有別的東西?

Event EventTarget Node Document HTMLElement 區別

  • HTMLElement extends Element
  • Element extends Node , Document extends Node
  • Node extends EventTarget
  • Event是發生在dom的事件,其中target屬性爲 EventTarget | null
  • Node有很多dom的API,Document 與Element又進行擴充,像className就是Element特有的,document有cookie,domain之類特有的。
  • 而HTMLElement就是擴充自Element,比Element多了offsetHeight之類屬性。

自定義事件

  • 瞭解了上面關係,再說下主角,就是自定義事件,就是這個特性的存在,導致不好判斷這個事件對象到底是個啥。
  • 我們以MouseEvent舉例,通常情況下,document.addEventListener('click',listener)這個語句中,listener就會接收到Event類型的參數,這時e.target的類型是EventTarget | null

類型能否是HTMLElement?

  • 答案肯定是不行,我們可以使用自定義事件傳個負值過去,就像這樣:
document.dispatchEvent(new MouseEvent('click',{clientX:-1,clientY:-1}))
  • 或者這樣:
document.dispatchEvent(new MouseEvent('click',{target:'123'}))
  • 這樣找不到target,target就會變成document,而根據第一節,document是Document類型,比HTMLElement類型要大,所以不行。

類型能否是Node?

  • 根據第一節可以發現,這個EventTarget有很多屬性沒有,是個比Node還大的玩意,那麼可能產生不是Node但是卻屬於EventTarget的東西嗎?這還真有,MDN舉出了3種特殊例子, XMLHttpRequest,AudioNode,AudioContext。XMLHttpRequest extends XMLHttpRequestEventTarget extends EventTarget可以看見,這個XMLHttpRequest既不屬於Node,但是卻屬於EventTarget,滿足了要求。另外2個不舉例了。
  • 雖然滿足了要求,但一個click事件怎麼會給你傳一個XMLHttpRequest的???這怎麼看也不對啊,就算你點的再偏,頂多給你傳個document,怎麼會傳XMLHttpRequest的?
  • 可以看一下這個騷操作:
let a=new XMLHttpRequest
a.addEventListener('click',(e)=>console.log(e))
let event = new Event('click')
a.dispatchEvent(event)
  • 詭異的XMLHttpRequest居然可以用addEventListener,最後e的打印結果:
bubbles: false
cancelBubble: false
cancelable: false
composed: false
currentTarget: XMLHttpRequest {onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
defaultPrevented: false
eventPhase: 0
isTrusted: false
path: []
returnValue: true
srcElement: XMLHttpRequest {onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
target: XMLHttpRequest {onreadystatechange: null, readyState: 0, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
timeStamp: 4900758.515000053
type: "click"
  • 這就使得target並不是Node類型,而是更大的EventTarget類型。
  • 有人就會說,那TS咋不能直接看調用者來進行更細的判斷,如果調用者是Node,那麼它的listener的target就應該是Node類型,如果調用者是XMLHttpRequest,那麼它的 listener的target就應該是XMLHttpRequest類型。
  • 沒錯,TS的特性其實是可以辦到的,但是目前情況就是所有的類型全部都由EventTarget擴展,而target屬性全部用的都是Event上的Target屬性。
  • 最好的解決方法就是對EventTarget添加泛型支持,這樣只要傳入相應的泛型,就能直接獲得想要的對象,這個問題存在了近5年還沒有解決,並且問題仍然在開放中,根據作者描述,他們的聲明文件是通過軟件自動生成的,這個源文件不能放出來給你看,另外,如果要添加泛型,會導致過不了性能測試。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章