Перейти к содержимому

Variance Modifiers: in, out

Свежая фича для подсказок компилятору и читабельности библиотек. Используют редко, но на senior-интервью могут спросить. Главное — понимать сами вариантности: covariance, contravariance, invariance, bivariance. Красный флаг — путать применение к классам и к параметрам типа.

Картинка вариантности — куда «течёт» подтипирование:

  Subtype  Cat <: Animal

Covariant   (out)   F<Cat>     <:  F<Animal>     ───▶  same direction
Contravar.  (in)    F<Animal>  <:  F<Cat>        ◀───  reversed
Invariant   (in out)  F<Cat>  ⟂  F<Animal>       ─×─  unrelated
Bivariant  (legacy)  both directions             ⇄    unsound
// out — параметр в позиции возврата (covariant)
interface Producer&lt;out T&gt; {
produce(): T;
}
const p1: Producer&lt;Animal&gt; = { produce: () =&gt; new Animal() };
const p2: Producer&lt;Cat&gt; = { produce: () =&gt; new Cat() };
const widened: Producer&lt;Animal&gt; = p2; // ок: Cat &lt;: Animal =&gt; Producer&lt;Cat&gt; &lt;: Producer&lt;Animal&gt;
class Animal { name = '' }
class Cat extends Animal { meow() {} }

in — параметр в позиции аргумента (contravariant):

interface Consumer&lt;in T&gt; {
consume(x: T): void;
}
const c1: Consumer&lt;Animal&gt; = { consume: a =&gt; console.log(a.name) };
const narrow: Consumer&lt;Cat&gt; = c1; // ок: Animal-consumer умеет принимать Cat

in out — invariant; используют для callback-ориентированных интерфейсов (Subject/State):

interface Cell&lt;in out T&gt; {
get(): T;
set(v: T): void;
}
// Cell&lt;Cat&gt; и Cell&lt;Animal&gt; не присваиваются ни в одну сторону.

Итог: in/out — подсказки компилятору и валидация: TS проверит, что вариантность параметра реально такая.