Typescript 中的逆变与协变
概念:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型 。 泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。
一、typescript 协变
示例
type parent = string|number|boolean
type child = string|boolean
type test = child extends parent ? true:false
// test 的类型是true
let a:child = true;
let b:parent = 1;
a = b; // 报错 Type 'number' is not assignable to type 'child'.
b = a; // 不报错
结论: 如图中代码所示 child类型可以继承parent类型(child 为 parent的子类) 当我们创建两个变量 a = true、b = 1并且分别赋给它们child和parent类型的时候。如果这这时想把b赋值给a,就会报类型错误(Type ‘number’ is not assignable to type ‘child’.),说child里面没有
number属性,而a赋值给b的时候不报错,说明变量赋值只能给它赋一个相同或者更具体的类型,这种特性称之为协变
二、typescript 逆变
示例
type parent = string|number|boolean
type child = string|boolean
type fun1 = (a:child)=> null;
type fun2 = (a:parent)=> null;
type test = fun2 extends fun1?true:false
// test 的类型是true
let fun11:fun1 = (a:child) => null;
let fun22:fun2 = (a:parent) => null;
fun11 = fun22; // 成功赋值
fun22 = fun11; // 报错 Type '(a: child) => null' is not assignable to type '(a: parent) => null'.
结论: 如图中代码所示 第一个方法类型中的参数类型为child,第二个方法中的参数类型为parent,test 的类型为true,说明了fun2类型可以继承fun1类型,且当我们实现两个这样的方法fun11和fun22以后,同样也只能由fun22给fun11赋值。
这样引出来一个问题:明明child继承自parent,为什么只能由参数为parent
的函数赋值给参数为child类型的函数呢?
案例:
type parent = string|number
type child = string
type fun1 = (a:child)=> void;
type fun2 = (a:parent)=> void;
let fun11:fun1 = (a:child) => {
switch (typeof a) {
case 'string':
console.log('string');
break;
default:
console.log('default');
break;
}
};
let fun22:fun2 = (a:parent) => {
switch (typeof a) {
case 'string':
console.log('string');
break;
case 'number':
console.log('number');
break;
default:
console.log('default');
break;
}
};
fun11 = fun22; // 成功赋值
fun22 = fun11; // 报错 Type 'number' is not assignable to type 'string'.
图中代码不难看出: 子类型的方法只能处理string,而父类型的方法不仅可以处理string,还能处理number。如果我们给fun11 赋值 fun22,fun22本来就能处理string和number,因此只给它赋值string类型的参数也不会有什么问题,反过来给fun11只能处理string类型的参数,但如果给fun22赋值fun11就有可能会往参数中传递number 类型,这样就会报错,这种逆向派生类型赋值的特性我们称之为逆变。