Загрузка…
Загрузка…
TypeScript · senior · сложность 9
Assignability в TypeScript структурная, не номинальная: A подходит там, где ждут B, если у A есть всё нужное B. Variance — правила для контейнеров: Array<Dog> usable как Array<Animal> (covariant), а функция, принимающая Animal, usable там, где ждут принимающую Dog (contravariant по параметрам). TS сознательно делает method parameters bivariant — исторический компромисс.
Structural typing: совпали члены — типы совместимы, имена классов не важны (кроме private/protected полей — они делают номинальный оттенок).
Array в строгой модели).Понимание variance объясняет странные ошибки assignability у колбэков и generics.
Dog <: Animal (Dog is a subtype of Animal)
Array<Dog> <: Array<Animal> ✅ covariant — flows the same direction
(x: Animal)=>void <: (x: Dog)=>void ✅ contravariant — flows the OPPOSITE directioninterface Handler<T> { handle(x: T): void } // method → bivariant
type HandlerFn<T> = { handle: (x: T) => void } // property fn → contravariantclass Animal { legs = 4 }
class Dog extends Animal { bark() {} }
// Covariance — return/read positions flow with the subtype
const dogs: Dog[] = [new Dog()];
const animals: Animal[] = dogs; // ✅ Array is (unsoundly) covariant
// The classic hole this opens:
animals.push(new Animal()); // ✅ compiles… but dogs now holds a non-Dog
dogs[1].bark(); // 💥 runtime crash — bark is undefined
// Contravariance — a broader consumer is a valid narrower consumer
type Listener = (e: MouseEvent) => void;
const broad = (e: Event) => {};
const l: Listener = broad; // ✅ accepting more is safe
// strictFunctionTypes catches the UNSAFE direction on function-typed params:
type Narrow = (e: MouseEvent) => void;
const wide: (e: Event) => void = (e: MouseEvent) => e.button; // ❌ error under strictСовместимость по структуре + variance контейнеров определяют, когда один тип подставляется вместо другого; bivariance методов — известный компромисс TS.