TypeScript 中協變/逆變類型分配的意外行為
我對 TypeScript 是新手,並且我發現了一些關於協變/逆變行為的意外情況。
這裡有一段程式碼片段:
interface Func1 {
(): { prop1: string }
}
// assignment similar to covariance
var f1: Func1 = function () { return { prop1: "", prop2: 0 }; }
interface Func2 {
({prop1: string, prop2: number}): void;
}
// assignment similar to contra-variance
var f3: Func3 = function (a: { prop1: string }) { }
interface Func3 {
({prop1: string}): void;
}
// assignment violates principle of contra-variance.
// expect a compiling error but there isn't.
var f3: Func3 = function (a: { prop1: string, prop2: number }) { alert(a.prop2); }
// here occurs a non-existing field accessing
// it might be unexpected and was possible to be eliminated by static checking on assignment to 'f3'.
f3({ prop1: "" });
將值指派給 f1 是可以的,因為匿名函數的返回值可以指派給 Func1 的返回值類型。
將值指派給 f2 也沒問題,因為傳給 Func2 類型的參數可以指派給匿名函數的參數 'a'。
分配給 f3 應該會失敗,因為傳給 Func3 類型的參數無法分配給匿名函數的參數 'a',所以我預期編譯器會報錯,但實際上並沒有。
這在呼叫 f3 時導致了意外的存取違規。
我的問題是,這種行為是預期的,還是 TypeScript 設計/實現上的缺陷?
答案#1
// 賦值違反了反變原則。 /預期會有編譯錯誤,但沒有。
這是常見問題,並且有文檔記錄: https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Type%20Compatibility.md#function-argument-bivariance
基本上,它允許方便地添加事件監聽器(這是一個相當常見的 JavaScript 任務,如果語言將安全性推得更高,移植到 TypeScript 會變得困難)。