返回
漫谈TypeScript之类型兼容与协变、逆变、双向协变
前端
2023-11-05 00:29:18
TypeScript中的类型兼容性
TypeScript中的类型兼容性是基于结构类型的,这意味着两个类型是否兼容取决于它们的组成结构是否相同。这种兼容性检查方式也称为“鸭式辨型法”,它关注的是对象的实际行为,而不是它们的类型名称。
例如,以下代码中的两个类型Person
和Employee
是兼容的,因为它们具有相同的结构:
interface Person {
name: string;
age: number;
}
interface Employee {
name: string;
age: number;
salary: number;
}
const person: Person = {
name: "John",
age: 30
};
const employee: Employee = {
name: "Jane",
age: 35,
salary: 100000
};
// 因为Person和Employee是兼容的,所以可以将employee赋值给person变量
person = employee;
协变、逆变和双向协变
协变、逆变和双向协变是TypeScript中类型之间关系的三种方式。
-
协变 :协变类型允许子类型的值可以赋值给父类型。例如,如果
Animal
是一个父类,Dog
是一个子类,那么Dog
类型的变量可以赋值给Animal
类型的变量。 -
逆变 :逆变类型允许父类型的值可以赋值给子类型。例如,如果
Animal
是一个父类,Dog
是一个子类,那么Animal
类型的变量可以赋值给Dog
类型的变量。 -
双向协变 :双向协变类型允许子类型的值可以赋值给父类型,父类型的值也可以赋值给子类型。例如,如果
Animal
是一个父类,Dog
是一个子类,那么Dog
类型的变量可以赋值给Animal
类型的变量,Animal
类型的变量也可以赋值给Dog
类型的变量。
协变、逆变和双向协变的应用
协变、逆变和双向协变在TypeScript中有很多应用场景,例如:
- 函数参数和返回值的协变 :函数的参数和返回值可以是协变的,这可以使函数更加灵活。例如,以下代码中的函数
max
可以接受任何实现了Comparable
接口的类型作为参数,并返回最大值:
interface Comparable {
compareTo(other: Comparable): number;
}
function max<T extends Comparable>(a: T, b: T): T {
if (a.compareTo(b) > 0) {
return a;
} else {
return b;
}
}
const x: number = max(1, 2); // 可以正常调用,因为number实现了Comparable接口
const y: string = max("hello", "world"); // 也可以正常调用,因为string也实现了Comparable接口
- 数组的协变 :数组的类型可以是协变的,这可以使数组更加灵活。例如,以下代码中的数组
numbers
可以存储任何类型的数字:
const numbers: Array<number> = [1, 2, 3];
numbers.push(4.5); // 可以正常添加,因为4.5也是数字
const value = numbers[0]; // value的类型为number,因为数组的类型是协变的
- 泛型的协变和逆变 :泛型的类型参数可以是协变的或逆变的,这可以使泛型更加灵活。例如,以下代码中的泛型类
Stack
可以存储任何类型的元素:
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.push(3);
const value = stack.pop(); // value的类型为number,因为泛型类Stack的类型参数T是协变的
总结
协变、逆变和双向协变是TypeScript中重要的概念,理解这些概念有助于您编写出更健壮、更灵活的代码。希望本文对您有所帮助。