Typescript 中的协变和逆变
2018-10-28 本文已影响18人
一大碗豆浆
Typescript的协变和逆变和C# Scala中的类似,但是Typescript的会自动算出来接口属于协变还是逆变,C# Scala中需要显示声明in out标记接口。 在typescript需要在tsconfig中使用strictFunctionTypes参数开启逆变检查,否则就是双变(协变或者逆变)。
逆变接口
接口中泛型只作为函数类型参数
interface Animal {
Eat(): void
}
interface Dog extends Animal{
Bark():void
}
interface Cat extends Animal{
Meow():void
}
interface Comparer<T> {
compareA: (a: T, b: T) => number;
}
逆变接口的赋值类型检查
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer;
/*
错误, 因为调用 animalComparer(Dog)的时候,
dogComparer会接受到一个Animal类型的参数,这是有风险的
*/
dogComparer = animalComparer; // 正确
例外情况
interface Comparer<T> {
compareA(a: T, b: T): number;
}
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer; //正确
dogComparer = animalComparer; // 正确
/*
因为
compareA(a: T, b: T): number;
和
compareA: (a: T, b: T) => number;
不太一样,前者认为是双变, 后者认为是逆变。这样做的相当于把方法类型的声明排除在外,目的在于为了确保带泛型的类和接口(如 Array)总体上仍然保持协变。
*/
协变接口
接口中泛型只作为函数类型返回值
interface Animal {
Eat(): void
}
interface Dog extends Animal{
Bark():void
}
interface Cat extends Animal{
Meow():void
}
interface Comparer<T> {
compareB: () => T;
}
协变接口的赋值类型检查
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer;
// 正确
dogComparer = animalComparer;
/*
错误, 因为调用 dogComparer(Dog)的时候,
animalComparer会返回一个Animal类型的值,这是有风险的
*/