Skip to content

协变和逆变

协变

子类型赋值给父类型的情况就叫做协变

比如有两个以下两个类型

typescript
interface Person {
    name: string;
    age: number;
} 

interface Musi {
    name: string;
    age: number;
    hobbies: string[]
}

Musi 是 Person 的子类型,更具体,那么 Musi 类型可以赋值给 Person 类型

typescript
let person: Person = {
	name: '',
    age: 23
}

let musi: Musi = {
	name: 'musi',
    age: 23,
    hobbies: ['sing', 'play game']
}

person = musi // ok

逆变

父类型赋值给子类型的情况就叫做逆变,只有函数的参数有逆变性质

比如有两个以下两个函数

typescript
let printHobbies: (musi: Musi) => void
printHobbies = (musi) => {
	console.log(musi.hobbies)
}

let printName: (person: Person) => void
printName = (person) => {
	console.log(person.name)
}

printHobbies = printName // ok

printName = printHobbies // error

printName 的参数 Person 不是 printHobbies 的参数 Musi的父类型么,为啥能赋值给子类型?

因为这个函数调用的时候是按照 Musi来约束的类型,但实际上函数只用到了父类型 Person 的属性和方法,当然不会有问题,依然是类型安全的。

那反过来呢,如果 printHoobies 赋值给 printName 会发生什么?

因为函数声明的时候是按照 Person 来约束类型,但是调用的时候是按照 Musi 的类型来访问的属性和方法,那自然类型不安全了,所以就会报错

类型父子关系的判断

在 ts 中,只要结构上是一致的,那么就可以确定父子关系,这种叫做结构类型系统

typescript
interface Person {
    name: string;
    age: number;
} 

interface Musi {
    name: string;
    age: number;
    hobbies: string[]
}

Person 和 Musi 没有 extends 关系,但在 ts 上,它们确实是父子关系

通过结构确定父子关系,更具体的那个是子类型

注意,这里用的是更具体,而不是更多。

判断联合类型父子关系的时候, 'a' | 'b' 和 'a' | 'b' | 'c' 哪个更具体?

'a' | 'b' 更具体,所以 'a' | 'b' 是 'a' | 'b' | 'c' 的子类型。