您能否使用小而简单的 TypeScript 示例解释什么是方差、协方差、逆变和双方差?
[持续更新]
有用的链接:
Oleg Valter的另一个与该主题相关的好答案
Titian-Cernicova-Dragomir对*-riance 的很好解释
斯蒂芬博耶博客
Scala 文档- 用例子很好的解释
@Titian的回答1
@Titian的回答2
马克·西曼的文章
@jcalz解释
您能否使用小而简单的 TypeScript 示例解释什么是方差、协方差、逆变和双方差?
[持续更新]
有用的链接:
Oleg Valter的另一个与该主题相关的好答案
Titian-Cernicova-Dragomir对*-riance 的很好解释
斯蒂芬博耶博客
Scala 文档- 用例子很好的解释
@Titian的回答1
@Titian的回答2
马克·西曼的文章
@jcalz解释
方差与泛型类型相对于其类型参数的F<T>
变化T
方式有关。如果您知道T extends U
,那么方差会告诉您是否可以得出结论F<T> extends F<U>
,得出结论F<U> extends F<T>
,或者两者都不是,或两者兼而有之。
协方差意味着F<T>
和T
协变。即,F<T>
随(同方向)变化T
。换句话说,如果T extends U
,那么F<T> extends F<U>
。例子:
函数或方法类型与它们的返回类型共同变化:
type Co<V> = () => V;
function covariance<U, T extends U>(t: T, u: U, coT: Co<T>, coU: Co<U>) {
u = t; // okay
t = u; // error!
coU = coT; // okay
coT = coU; // error!
}
其他(暂时未说明)示例是:
逆变意味着F<T>
与T
相反的变化。也就是说,F<T>
与(从相反的方向)变化T
。换句话说,如果T extends U
,那么F<U> extends F<T>
。例子:
函数类型与其参数类型相反(--strictFunctionTypes
启用):
type Contra<V> = (v: V) => void;
function contravariance<U, T extends U>(t: T, u: U, contraT: Contra<T>, contraU: Contra<U>) {
u = t; // okay
t = u; // error!
contraU = contraT; // error!
contraT = contraU; // okay
}
其他(暂时未说明)示例是:
不变性意味着F<T>
既不随 也不随 变化T
:F<T>
在 中既不协变也不逆变T
。这实际上是最一般情况下发生的情况。协变和逆变是“脆弱的”,因为当你结合协变和逆变类型函数时,它很容易产生不变的结果。例子:
返回与其参数相同类型的函数类型在该类型中既不协变也不反变:
type In<V> = (v: V) => V;
function invariance<U, T extends U>(t: T, u: U, inT: In<T>, inU: In<U>) {
u = t; // okay
t = u; // error!
inU = inT; // error!
inT = inU; // error!
}
双变量意味着F<T>
随着和反对的变化:在 中既是协变的也不是逆变的。在健全的类型系统中,这对于任何非平凡的类型函数基本上都不会发生。您可以证明只有像这样的常量类型函数才是真正的双变量(快速草图:对于 all ,so和,并且在健全的类型系统中 if和,then与 相同。所以 if = for all ,then是常量) .T
F<T>
T
type F<T> = string
T extends unknown
T
F<T> extends F<unknown>
F<unknown> extends T
A extends B
B extends B
A
B
F<T>
F<unknown>
T
F<T>
但是 Typescript 没有也不打算拥有一个完全健全的类型系统。还有一个值得注意的案例,TypeScript 将类型函数视为双变量:
方法类型与它们的参数类型共同变化和反向变化(这也发生在所有--strictFunctionTypes
禁用的函数类型中):
type Bi<V> = { foo(v: V): void };
function bivariance<U, T extends U>(t: T, u: U, biT: Bi<T>, biU: Bi<U>) {
u = t; // okay
t = u; // error!
biU = biT; // okay
biT = biU; // okay
}