網站搜尋

typescript - 這個比較似乎是無意的,因為類型 'this' 和 'Derived' 沒有重疊


我有一個類別 Base 和一個從它繼承的類別 Derived。Base 有一個方法,它接受一個 Derived 作為參數並將 this 與它進行比較:

class Base {
    foo(bar: Derived): void {
        if(this === bar){
            return;
        }
        //Do stuff
    }
}

class Derived extends Base {
    baz(): void {}
}

遊樂場連結

當我嘗試編譯它時,我收到這個錯誤:

error TS2367: This comparison appears to be unintentional because the types
 'this' and 'Derived' have no overlap.

3         if(this === bar){
             ~~~~~~~~~~~~

我不明白為什麼我會得到這個錯誤,this 和 Derived 都是 Base 類型,所以它們確實有重疊。如果我做 const x=new Derived(); x.foo(x);,它會進入 if 區塊,所以這個比較並不像編譯器看起來認為的那樣沒用。

有趣的是,如果我移除 baz() 方法,使 Derived 的內容為空,錯誤就消失了(但這並不能解決我的問題,因為擁有一個空內容的類別沒有太大意義)。

為什麼我會收到這個錯誤,我該如何解決它?

答案#1

錯誤訊息的措辭可能具有誤導性。將「沒有重疊」解釋為不可能存在同時為 this 類型和 Derived 類型的值是合理的,但這顯然不正確。以前的錯誤訊息甚至更糟,會說「此比較總是 false」,而不是「這似乎是無意的」。這已經改過,參見 microsoft/TypeScript#27910,但對「沒有重疊」的困惑仍然存在,人們也對此提出了問題,例如 microsoft/TypeScript#60583、microsoft/TypeScript#44645 和 microsoft/TypeScript#37155。其中一些被歸類為錯誤,但通常當 TS 團隊提供解釋時,他們認為錯誤的存在本身不是錯誤,但 訊息 可以改進。

實際上發生的是,TypeScript 有各種啟發式方法來判斷像 === 這樣的比較運算符的使用是否可能是有意的或者不太可能是有意的。粗略地說,規則是:如果 expr1 的類型與 expr2 的類型兼容,或者反過來,則允許使用 expr1 === expr2。這裡的“兼容”意思類似於“可賦值給”或“expr2 的子類型”或“一種類型縮小”。細節並不是特別重要。

而在你的情況下,你不能將 this 指派給 Derived 類型的值,也不能將 bar 指派給 this 類型的值,所以兩者都不能相互指派。因此 TypeScript 將此視為你的比較可能是無意的跡象。有類似的情況,開發者會很高興看到那個錯誤訊息,因為他們確實寫錯了。正如 microsoft/TypeScript#44645 的一條評論中提到的:

這些就是那種檢查,只要我們稍微加嚴一點,就能在真實程式碼中發現大量實際的錯誤。

使用 === 可能會犯各種錯誤,這些錯誤雖然不能被 證明 永遠為假,但很可能不是刻意為之。在奇怪的代碼中產生少量誤報以在錯誤代碼中獲得真正的正確報告,是所有類型檢查器有意為之的取捨。

在 TypeScript 將你的比較標記為可能非故意,但你確實是有意這樣做的情況下,你可以將比較的一方 擴寬 為另一方的超型別。正如你所說,this 和 Derived 都是 Base 類型。因此將任一方擴寬為 Base,錯誤就會消失:

if (this as Base === bar) { }
if (this === bar as Base) { }

最簡單的方法是使用類型斷言來擴寬,但由於類型斷言也可能不安全地縮窄,您可能更傾向於避免使用它,或搭配更安全的方法來使用:

const thisBase: Base = this; if (thisBase === bar) { }
if (this satisfies Base as Base === bar) { }

無論如何,你都必須經過一些小障礙來說服 TypeScript,你這麼做是有意的。這不是最佳的做法,但每當你遇到「哎呀,這顯然是故意的」時,實際上也會有不少「哦,這其實是個錯誤」。這類事情本質上總是啟發式的,而且由於無法完美捕捉開發者的意圖,它們能做的最好的事情就是試著找到那個權衡能最大化平均開發者體驗的點。

程式碼的 Playground 連結