逆变、协变、双向协变、不变
类型安全和型变
我们都知道在ts中,我们可以给变量定义为不同的类型,例如number
类型和string
类型。但是,除了这种死板的类型限制,还有一些灵活的类型限制。比如子类型是可以赋值给父类型的。这就是型变(类型改变)。这种型变分为两种。一种是子类型赋值给父类型叫做协变
。一种是父类型赋值给子类型叫做逆变
。
协变
interface Person{
name: string,
age: number
}
interface Student{
name: string,
age: number,
certificate: number
}
let person:Person = {
name: 'name',
age: 1
}
let student:Student = {
name: 'name2',
age: 2,
certificate: 2
}
person = student;
逆变
interface Person{
name: string,
age: number
}
interface Student{
name: string,
age: number,
certificate: number
}
let printHobbies:(student: Student)=>void;
let printName:(person: Person)=>void;
printHobbies = (student)=>{
console.log(student);
}
printName = (person)=>{
console.log(person);
}
printHobbies = printName;
// printName = printHobbies;//不能将类型“(student: Student) => void”分配给类型“(person: Person) => void”。
为什么父类型可以赋值给子类型呢?因为这个函数用的是Student来约束类型的,但实际上函数如果只用了父类型Person的属性和方法,也不会有问题,依然是安全的类型。函数的参数是逆变,返回值是协变。
type Func = (a: string) => void;
const func: Func = (a: 'hello') => undefined//报错
//因为string不是'hello'的子类型,所以报错
type Func = (a: string) => void;
const func: Func = (a: string) => undefined
//因为undefined是void的子类型,所以不报错
双向协变
在ts2.x之前支持这种赋值,也就是父类型可以赋值给子类型,子类型也可以赋值给父类型。既逆变又协变。叫做双向协变
。双向协变
不能保证类型安全,所以,之后ts加了一个编译选项,strictFunctionTypes,设置为true就支持函数的逆变,设置为false就支持双向协变。
不变
非父子类型之间不会发生型变,只要类型不一样就会报错。
类型父子关系的判断
在java中,如果A extends B,那么A就是B的子类型。(名义系统类型)在ts中,只要结构上是一致的,那么就可以确定父子关系。(结构类型系统)