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<out T> { produce(): T;}const p1: Producer<Animal> = { produce: () => new Animal() };const p2: Producer<Cat> = { produce: () => new Cat() };const widened: Producer<Animal> = p2; // ок: Cat <: Animal => Producer<Cat> <: Producer<Animal>
class Animal { name = '' }class Cat extends Animal { meow() {} }in — параметр в позиции аргумента (contravariant):
interface Consumer<in T> { consume(x: T): void;}const c1: Consumer<Animal> = { consume: a => console.log(a.name) };const narrow: Consumer<Cat> = c1; // ок: Animal-consumer умеет принимать Catin out — invariant; используют для callback-ориентированных интерфейсов (Subject/State):
interface Cell<in out T> { get(): T; set(v: T): void;}// Cell<Cat> и Cell<Animal> не присваиваются ни в одну сторону.Итог: in/out — подсказки компилятору и валидация: TS проверит, что вариантность параметра реально такая.