返回
深入浅出:TS中的逆变与协变揭秘
前端
2023-11-09 00:02:51
在 TypeScript 中驾驭协变和逆变:揭开泛型关系的面纱
在 TypeScript(TS)中,泛型是一项强大的功能,它允许开发者创建可重用的组件和类型,而无需指定特定的类型参数。然而,当涉及到子类型和父类型之间的关系时,理解协变和逆变的概念至关重要。
协变与逆变:子类型和父类型的泛型关系
协变和逆变了泛型类型参数如何在子类型中比父类型更宽泛或更受限制。
协变 允许子类型的泛型参数类型比父类型更宽泛。这意味着子类型可以接收父类型无法接收的值。
逆变 与协变相反,它允许子类型的泛型参数类型比父类型更受限制。这意味着子类型只能接收父类型能够接收的值。
协变的应用
协变在以下场景中很有用:
- 当你想扩展父类型的功能时,同时保持子类型能够处理父类型的所有值。
- 例如,考虑一个表示动物的基类 Animal 和一个表示狗的子类 Dog。Dog 继承了 Animal 的所有属性,但它还具有 breed 属性。我们可以使用协变来允许一个 Dog 数组安全地赋值给一个 Animal 数组,因为 Dog 兼容于 Animal。
逆变的应用
逆变在以下场景中很有用:
- 当你想缩小父类型的输入范围时,同时确保子类型可以处理父类型的所有输入。
- 例如,考虑一个过滤器接口 Filter
,它表示一个可以对类型 T 的值进行过滤的函数。StringFilter 是 Filter 的一个子类型,它专门处理字符串。我们可以使用逆变来允许一个 StringFilter 安全地赋值给一个 Filter,因为 StringFilter 只处理字符串,而 Filter 可以处理任何类型。
示例
让我们通过代码示例进一步说明协变和逆变:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
let animals: Array<Animal>;
let dogs: Array<Dog>;
// 协变允许将 Dog 数组赋值给 Animal 数组
animals = dogs;
// 逆变允许将 StringFilter 赋值给 Filter
interface Filter<T> {
(value: T): boolean;
}
interface StringFilter extends Filter<string> {
(value: string): boolean;
}
let filter: Filter<any>;
let stringFilter: StringFilter;
filter = stringFilter;
什么时候使用协变或逆变?
选择使用协变还是逆变取决于特定场景。遵循以下准则:
- 使用协变: 当你扩展父类型的功能时。
- 使用逆变: 当你缩小父类型的输入范围时。
结论
协变和逆变是理解 TS 中泛型关系的关键概念。正确使用这些特性可以提高代码的可重用性和安全性。
常见问题解答
- 协变和逆变有什么区别?
协变允许子类型的泛型参数类型比父类型更宽泛,而逆变允许子类型的泛型参数类型比父类型更受限制。
- 什么时候应该使用协变?
当你想扩展父类型的功能时,同时保持子类型能够处理父类型的所有值。
- 什么时候应该使用逆变?
当你想缩小父类型的输入范围时,同时确保子类型可以处理父类型的所有输入。
- 协变和逆变是如何影响类型兼容性的?
协变允许子类型的数组赋值给父类型的数组,而逆变允许父类型的变量赋值给子类型的变量。
- 如何正确地使用协变和逆变?
在 TS 中,协变和逆变通过指定泛型参数上的加号(+)和减号(-)来指定。加号表示协变,减号表示逆变。