Что значит контрвариантные функции
Документация тайпскрипта
Under strictFunctionTypes function type parameter positions are checked contravariantly instead of bivariantly. For some background on what variance means for function types check out What are covariance and contravariance?.
Прочитал и это тоже, но не понял что же такое контрвариантность?
Ответы (1 шт):
Ковариантность и контравариантность обобщенных интерфейсов:
Понятия ковариантности и контравариантности связаны с возможностью использовать в приложении вместо некоторого типа другой тип, который находится ниже или выше в иерархии наследования.
- Ковариантность позволяет использовать более конкретный тип, чем заданный изначально.
- Контравариантность позволяет использовать более универсальный тип, чем заданный изначально.
Таким образом, если мы создадим тип данных животные и его подтип собаки или кошки, то в наших функциональных типах разные параметры будут по разному реагировать на значение другого типа:
- ковариантный (covariant) позволяет передать ему
собакиликошеквместоживотных, назначенных изначально; - контрвариантный (contravariant) примет
животныхвместособак, но не наоборот; - бивариантный (bivariant) позволит оба варианта;
- инвариантный (invariant) не допустит подмен, только предписанный тип данных.
Обычно параметры в тайпскрипте позволяют инвариантность, а со strictFunctionTypes параметр потребует контрвариантности. Поэтому если, например:
type Animal = 'cat1' | 'cat2' | 'dog1' | 'dog2' | 'elephant1' | 'elephant2';
type Dog = 'dog1' | 'dog2';
declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
то для обычного параметра оба присвоения годятся (песочница):
f1 = f2; // Ok without --strictFunctionTypes
f2 = f1; // Ok
а cо strictFunctionTypes первое вызовет сообщение об ошибке (песочница):
f1 = f2; // Error with --strictFunctionTypes
f2 = f1; // Ok
Если же добавить третий подтип
type Cat = 'cat1' | 'cat2';
declare let f3: (x: Cat) => void;
то третье присвоение вызовет ошибку в обоих случаях — и со strictFunctionTypes, и без него:
f2 = f3; // Error
Интерфейсы
Для интерфейсов важно, есть ли между ними структурное отличие, просто факта наследования недостаточно.
TypeScript’s structural type system was designed based on how JavaScript code is typically written. Because JavaScript widely uses anonymous objects like function expressions and object literals, it’s much more natural to represent the kinds of relationships found in JavaScript libraries with a structural type system instead of a nominal one.
Вот такой код не покажет ошибок (песочница):
interface Animal{}
interface Dog extends Animal{}
interface Cat extends Animal{}
declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2;
f2 = f1;
f2 = f3;
А вот в таком коде будет одна ошибка без strictFunctionTypes или две ошибки, если этот параметр включён в настройках:
interface Animal{}
interface Dog extends Animal{
breed: string;
}
interface Cat extends Animal{
name: string;
}
declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2; // Error with --strictFunctionTypes
f2 = f1; // Ok
f2 = f3; // Error
И текст ошибки:
Type 'Animal' is not assignable to type 'Dog'.