詳解 TypyScript 的一個怪異行為

詳解 TypyScript 的一個怪異行為

作者 | Matthew Miller

最近我在閱讀 Asana 的博客時,看到了一篇關於 TypeScript 怪異行為的文章(https://blog.asana.com/2020/01/typescript-quirks/),文中提到的第一條 TypeScript 的怪異行為引起了我的興趣。儘管看上去似乎前後不一致,但實際上這種類型系統的行為完全合乎邏輯。

該文章使用了接口 Dog 和函數 printDog 作為例子:

<code>interface Dog {
breed: string
}

function printDog(dog: Dog) {
console.log("Dog: " + dog.breed)
}
/<code>

這段代碼模擬了 TypeScript 中的一種流行寫法。似乎到目前為止沒什麼問題,但這篇文章介紹了一個看似“前後不一致”的情況。儘管你可以把一個包含 breed 屬性的對象先賦值給一個變量再傳遞給該函數,但你不能直接將這個對象傳遞給該函數。

<code>const ginger = {
breed: "Airedale",
age: 3
};
printDog(ginger); //works

printDog({
breed: "Airedale",
age: 3
}); //fails/<code>

這是為什麼?TypeScript 會檢查對象中的額外屬性,並發現bug,但為什麼只有在直接將對象傳遞給函數時才會出問題?

這篇文章指出,TypeScirpt 是一個“結構化類型語言”,也就是說類型檢查是根據對象的結構進行的,而不是根據它的繼承關係進行的。但這條規則有一個例外,那就是當開發明確指定變量類型的時候。而且 TypeScript 函數的參數是協變的,這意味著它可以接受任何子類型。在結構化類型語言中,給已有類型添加一個屬性就相當於擴展該類型。

那麼,這個錯誤是為什麼呢?如果它允許傳遞任何指定類型的子類型,那麼為什麼有時候會出錯?

要回答這個問題,需要回到我之前提到的類型系統上。在創建一個變量並傳遞給函數時,變量會推斷一個類型。在創建函數中的對象時,我們明確告訴 TypeScript 變量是 Dog 類型。創建變量時會檢查類型是否包含多餘屬性,但函數調用時不會,因為函數調用是協變的。這個“不一致”與函數調用也沒關係,只要在創建對象時指定類型就會產生同樣的行為:

<code>const ginger: Dog = {
breed: "Airedale",
age: 3
};/<code>

這段代碼會產生與函數類型相同的錯誤,因為age並不是Dog類型的屬性。

雖然類似的問題經常會困擾 TypeScript 開發者,但類型系統的任何行為通常都有合理的解釋。如果沒有,那你就應該向微軟報告錯誤。

原文:https://matthewmiller.dev/blog/demystifying-typescript-quirk/

本文為 CSDN 翻譯,轉載請註明來源出處。


分享到:


相關文章: