Skip to content

Latest commit

 

History

History
91 lines (65 loc) · 2.56 KB

逆变和协变.md

File metadata and controls

91 lines (65 loc) · 2.56 KB

类型兼容性、逆变和协变、双向协变以及不变性

先来看个问题

const foo = (data: {a: number, b: string}): void => console.log(data);
let data1: {a: number} = {a: 1};
let data2: {a: number, b: string, c: string[]} = {a: 1, b: 'b', c: []};

// 请说出下面4个的结果
foo(data1)
foo(data2)
data1 = data2
foo(data1)

类型兼容性

子类型比父类型更加具体,父类型比子类型更宽泛,所以子可以兼容父

// 在集合论中,属性更少的集合是子集。
function f(val: { a: number; b: number }) {}
let val1 = { a: 1 }
let val2 = { a: 1, b: 2, c: 3 }
f(val1); // error
f(val2); // val2 是 val 的父类型

// 在类型系统中,属性更多的类型是子类型。如下面的 动物类
interface Animal {age: number}
interface Dog extends Animal {bark(): void}

let animal: Animal
let dog: Dog
animal = dog // ✅ok
dog = animal // ❌error! animal 实例上缺少属性 'bark'

逆变和协变

wiki: 协变与逆变(Covariance and contravariance )是在计算机科学中,描述具有父/子型别关系的多个型别通过型别构造器、构造出的多个复杂型别之间是否有父/子型别关系的用语。

简单说就是,具有父子关系的多个类型,在通过某种构造关系构造成的新的类型,如果还具有父子关系则是协变的,而关系逆转了(子变父,父变子)就是逆变的。

1. 协变

// 基于上面的 动物类
type Covariant<T> = T[];
let coSuperType: Covariant<SuperType> = [];
let coSubType: Covariant<SubType> = [];

coSuperType = coSubType; // 新的类型依然具有父子关系

2. 逆变

type Contravariant<T> = (p: T) => void;
let contraSuperType: Contravariant<SuperType> = function(p) {}
let contraSubType: Contravariant<SubType> = function(p) {}

contraSubType = contraSuperType; // 函数是逆变的,父子关系反转,如果集合

contraSuperType(subType);
contraSubType(superType); // error

3. ts中函数默认都是双向协变

开启了 tsconfig 中的 strictFunctionType 后才会严格按照 逆变 来约束赋值关系。

4. 不变性

class Animal { }
class Cat extends Animal {meow() {console.log('cat meow');}}
class Dog extends Animal {wow() { console.log('dog wow'); }}

let catList: Cat[] = [new Cat()];
let animalList: Animal[] = [new Animal()];
let dog = new Dog();

animalList = catList;
animalList.push(dog);
catList.forEach(cat => cat.meow()); // cat.meow is not a function

参考

sl1673495/blogs#54