Assignability & Variance
Зачем знать: ровно тут TS становится unsound по дизайну (массивы и методы — bivariant). Просят: «Почему Cat[] присваивается в Animal[], и почему это опасно?» — классический вопрос про covariant arrays. Красный флаг — путать assignability с identity.
Правила структурной совместимости для функций / массивов / readonly:
Параметры функции — CONTRAVARIANT (входы расширяются) Возврат функции — COVARIANT (выходы сужаются) Массивы / методы — BIVARIANT (TS unsound by design) readonly tuple — COVARIANT, mutable tuple — INVARIANT
class Animal { name = ''; }class Cat extends Animal { meow() {} }
// Covariant arrays — assignable, но unsound:const cats: Cat[] = [new Cat()];const animals: Animal[] = cats; // окanimals.push(new Animal()); // компилируется, ломает cats[i].meow() в runtime!
// strictFunctionTypes делает параметры функций контравариантнымиtype Handler<T> = (x: T) => void;let h1: Handler<Animal>;let h2: Handler<Cat> = (c) => c.meow();// h1 = h2; // ошибка под strictFunctionTypes — h1 могут вызвать с AnimalExcess property check vs assignability:
interface Btn { label: string }
function render(b: Btn) {}
// Присваиваемость через переменную работает (структура совпадает)const opt = { label: 'Ok', size: 'lg' };render(opt); // ок
// Литерал напрямую — excess property check ругается// render({ label: 'Ok', size: 'lg' });readonly-кортеж присваивается в covariant позицию шире, чем mutable; обратно — нельзя.
type RT = readonly [number, number];type MT = [number, number];
const m: MT = [1, 2];const r: RT = m; // ок: mutable -> readonly// const m2: MT = r; // ошибка: нельзя «забыть» readonlyИтог: Параметры — контравариантны (под strictFunctionTypes), методы и массивы — бивариантны (unsound), readonly расширяет совместимость.