Pull to refresh

Comments 14

Позвольте вставить мои 5 копеек. Тоже некоторое время обходил тему стороной, пока не набрался смелости и не разобрался в данной теме. На мой взгляд, с точки зрения C#:

Ковариантность — позволяет использовать более конкретный (производный) тип, чем указанный. Применима к возвращаемым параметрам. Сохранение иерархии наследования типов, передаваемых в качестве аргументов типа, в обобщенных типах в том же порядке. Так, если класс Cat потомок класса Animal, то список IEnumerable<Cat> потомок списка IEnumerable<Animal>. «Список кошек» — это частный случай «списка животных», в список животных можно занести кошку, а в список кошек занести любое животное (например, собаку) нельзя. Тип (в данном случае обобщённый интерфейс) IEnumerable<T> ковариантен своему параметру-типу T.

Контравариантность — позволяет использовать более универсальный (базовый) тип, чем указанный. Применима к принимаемым параметрам. Обращение иерархии наследования типов, передаваемых в качестве аргументов типа, на противоположную в обобщенных типах. Так, если класс Cat потомок класса Animal, а делегат Action<T> определён как метод, принимающий объект типа T, то Action<Animal> потомок делегата Action<Cat>, а не наоборот. Если «все коты — животные», то «всякий метод (например, сфотографировать), оперирующий животными, может выполнить операцию над котом». Но не наоборот — метод, оперирующий котами (например, подержать в руках), не может выполнить операцию над любым животным (например, слоном). В таком случае говорят, что тип (в данном случае обобщённый делегат) Action<T> контравариантен своему параметру — типу T.

Надеюсь, правильно разобрался. Или — что то не так? Буду благодарен за критику.

По мне, все так, правильно. Не вижу противоречий.


Кратко как я понял коммент — Ковариантность по результату, Контравариантность по аргументам, я так же понимаю.

Не совсем…
а в чем именно отличия ?

То есть вы прочитали, но отличий не увидели?

Я близорукий, не отрицаю

Извиняюсь, но вы говорите загадками, а у меня сейчас голова болит чтоб их отгадывать

«Список кошек» — это частный случай «списка животных», в список животных можно занести кошку, а в список кошек занести любое животное (например, собаку) нельзя.

Тут вы сами себе противоречите. Правильно было бы "элемент, доставаемый из списка кошек, будет животным, а вот гарантировать, что из списка животных мы всегда будем получать именно кошек, нельзя".


В этом противоречии собака-то и зарыта. IEnumerable<T> — это функция int -> T (получить элемент по индексу), с одной стороны, и функция (int, T) -> IEnumerable<T> (записать элемент типа T по индексу), с другой. Т.е. это функция и принимающая, и возвращающая T.

К моему удивлению TypeScript версии 4.2.4 не отрабатывает контр-вариантность в случае функций/лямбд

Надо указать в тсконфиге строгий режим, подробности тут: https://habr.com/ru/post/557738/
Тогда всё нормально проверяется.
Без строгого режима TS полезен только для автокомплита в IDE, но как тайпчекер совсем никакой.

На самом деле проблема даже не в строгом режиме, а в том, что в TypeScript — тот самый «базовый» тип — подтипом которого являются все остальные типы — не «any», а «unknown».

«any» — строго говоря вообще не тип, а хак, который отключает любую проверку типов в месте применения :)

gochaorg То есть если вы в приведенном примере замените «any» на «unknown» — то получите именно то поведение, которого ожидаете.

Для примера с any - согласен, замена на unknown решает вопрос.

А вот здесь дело именно в строгом режиме:

const _f1 : (x: Box) => boolean = a => true;
const _f3 : (x: Shape) => boolean = _f1; // здесь
Прошу прощения, но рациональное число — пара таких чисел m и n, что m — целое число, n — натуральное число, которое > 0. Если же мы говорим про множество несократимых рациональных чисел, то добавляем условие того, что наибольший общий делитель m и n = 1.

Хорошее дополнение, спасибо

Sign up to leave a comment.

Articles