持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
1. 引言
今天我们来介绍下什么是逆变与协变。首先在介绍之前我们先来了解下什么是父子类型,我们先来学习了解下 父子类型:
interface Animal {
name: string
}
interface Person extends Animal {
age: number
}
let an: Animal = {
name: '动物'
}
let pe: Person = {
name: '人类',
age: 25
}
an = pe // ok
pe = an // error:Property 'age' is missing in type 'Animal' but required in type 'Person'.(2741)
上述例子Person类型继承了Animal类型,并拥有自己单独的属性,相比于父类型更具体属性和行为更多,当我们使用赋值语句时,子类型完全可以赋值给父类型,反之则不然。我们用Person 《 Animal 说明他们的关系。
如果我们使用联合类型来举例说明呢,考考自己是否能理解清楚
type Parent = 'a' | 'b' | 'c'
type Son = 'a' | 'b'
let parent: Parent
let son: Son
son = parent
// × Error: Type 'Parent' is not assignable to type 'Son'.
// Type '"c"' is not assignable to type 'Son'.
parent = son
// ok
上述问题son是parent的子类型,parent相比于son范围更广,所以parent无法赋值给son类型,反之,可以
介绍完父子类型,我们还有了解一个什么是类型系统的类型转换,我们通过下面这个例子学习下
interface Human {
name: string
age: number
}
type demo = Pick<Human,'name'> // type demo = {name: string}
上述就是基础的类型转换案例,通过类型转换语法,转换目标类型
2. 含义
介绍完父子类型和类型转换,我们回归正题
什么是协变?
协变:在经过相同的类型转换之后,两者的父子关系不变。
什么是逆变?
逆变:在经过相同的类型转换之后,两者的父子关系互换。
3. 作用
协变:就是当你的类型在经过转换等处理后,还保持与其他类型在转换前的类型关系(请看例子一) 逆变:当你的类型在经过转换等处理后,转换后的类型和之前对比是互调的,通常就是函数方法的入参的位置就是逆变位(请看例子二)
4. 例子
例子一:
interface Human {
name: string
age: number
}
interface Animal {
name: string
}
type CovariantFun<T> = T[]
type TooarHuman = CovariantFun<Human>
type TooarAnimal = CovariantFun<Animal>
let humanDemo:TooarHuman = [{name: 'guozi',age: 18}]
let animalDemo:TooarAnimal = [{name: 'guozi'}]
animalDemo = humanDemo // ok
humanDemo = animalDemo // error :Type 'TooarAnimal' is not assignable to type 'TooarHuman'.Property 'age' is missing in type 'Animal' but required in type 'Human'.
例子二:
interface Human {
name: string,
age: number
}
interface Animal {
name: string
}
let human:Human = {
name: 'zzz',
age: 12
}
let animal:Animal = {
name: 'www'
}
animal = human // ok: human是animal 的子类型
human = animal // error: Property 'age' is missing in type 'Animal' but required in type 'Human'.
type HumanFun = (arg:Human) => void
type AnimalFun = (arg:Animal) => void
let humanDemo: HumanFun = (human: Human)=>{
console.log(human.name)
console.log(human.age)
}
let animalDemo: AnimalFun = (animal: Animal)=>{
console.log(human.name)
}
animalDemo = humanDemo // error: Type 'HumanFun' is not assignable to type 'AnimalFun'.
// Types of parameters 'arg' and 'arg' are incompatible.
// Type 'Animal' is not assignable to type 'Human'.(232
humanDemo = animalDemo // ok
由上述例子发现,类型在通过函数传参后父子关系由之前animal = human‘正确’ 变为animalDemo = humanDemo ‘错误’,这就是比较典型的“逆变”,==在上面的例子我们可以这么理解当作为函数的入参时humanDemo 接受两个入参,而animalDemo只接受一个入参,当函数只接受一个入参,你传两个,不行;如果接受两个入参,你传一个,可以==;
5. 总结
这篇文章的感悟是我学习 TS 途中遇到的一个问题查询资料并理解后所诞生的。如果有错误或疏漏欢迎大家批评指正 同时扩展下:infer 在协变和逆变的情况下是有不同现象的。