前言
学过集合论的同学一定知道子集的概念,使用ES6 class写过继承的同学一定知道子类的概念,而使用过TypeScript的同学,也许知道子类型的概念。
但是你知道协变 (Covariant)、逆变 (Contravariant)、双向协变 (Bivariant) 和不变 (Invariant) 这些概念吗?你知道像TypeScript这种强大的静态类型检查的编程语言,是怎么做类型兼容的吗?我们今天来聊聊。
关于Subtyping
子类型是编程语言中一个有趣的概念,源自于数学中子集的概念:
如果集合A的任意一个元素都是集合B的元素,那么集合A称为集合B的子集。
而子类型则是面向对象设计语言里常提到的一个概念,是继承机制的一个产物,以下概念来源百度:
在编程语言理论中,子类型是一种类型多态的形式。这种形式下,子类型可以替换另一种相关的数据类型(超类型,英语:supertype)。
子类型与面向对象语言中(类或对象)的继承是两个概念。子类型反映了类型(即面向对象中的接口)之间的关系;而继承反映了一类对象可以从另一类对象创造出来,是语言特性的实现。因此,子类型也称接口继承;继承称作实现继承。
我们可以理解子类就是实现继承,子类型就是接口继承,下面这幅图更精确的定义了这个概念,很多同学应该知道这个例子:
这幅图中,猫是一种动物,所以我们说猫是动物的子集,猫是动物的子类,或者说猫这种类型是动物这种类型的子类型。
Co…, Contra…, Bi…, Invariant?
一下提到四个陌生的单词,很多同学肯定一下就懵了。React开发者应该对HOC (High Order Component) 不陌生,它就是使用一个基础组件作为参数,返回一个高阶组件的函数。React的基础是组件 (Component),在TypeScript里是类型 (Type),因此我们用HOT (High Order Type) 来表示一个复杂类型,这个复杂类型接收一个泛型参数,返回一个复合类型。
下面我用一个例子来阐述这四个概念,你可以将它使用TypeScript Playground运行,查看静态错误提示,进行更深刻理解:
interface SuperType {
base: string;
}
interface SubType extends SuperType {
addition: string;
};
// subtype com