неожиданное поведение в ковариации / контра-дисперсии, как присвоение 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 должно быть fail, потому что аргумент, подаваемый в тип Func3, не может быть присваивается аргументу " a " анонимной функции, поэтому я ожидаю, что компилятор вызовет ошибку, но на самом деле это не так.

Это приводит к неожиданному нарушению доступа при вызове f3.

Мой вопрос в том, является ли поведение ожидаемым или это дефект дизайна/реализации typescript?

1 2

1 ответ:

// присвоение нарушает принцип контрдиверсии. // ожидайте ошибки компиляции, но ее нет.

Это распространенная проблема, и она документирована: https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Type%20Compatibility.md#function-argument-bivariance

В основном это позволяет удобно добавлять прослушиватели событий (довольно распространенная задача JS, которую было бы трудно перенести в TypeScript, если бы язык тянул слайдер дальше к безопасности)