TypeScript 中方差、协方差、逆变和双方差的区别
Difference between Variance, Covaraince, Contravariance and Bivariance in TypeScript
能否使用简单的 TypeScript 示例解释什么是方差、协方差、逆变和双方差?
[持续更新]
有用链接:
Another one good answer of Oleg Valter与主题相关
Very good explanation of*-riance by Titian-Cernicova-Dragomir
-
Scala documentation - 很好的解释和例子
@提香的
@提香的
-
-
@jcalz
Variance 与泛型类型 F<T>
varies 相对于其类型参数 T
.如果你知道 T extends U
,那么方差会告诉你是否可以得出 F<T> extends F<U>
,得出 F<U> extends F<T>
,或者两者都不能得出。
Covariance 表示 F<T>
和 T
co-vary.也就是说,F<T>
随 变化(与 T
的方向相同)。换句话说,如果T extends U
,那么F<T> extends F<U>
。示例:
函数或方法类型与其 return 类型共同变化:
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!
}
其他(暂时未显示)示例是:
- 对象在它们的 属性 类型中是协变的,即使这听起来不适合可变属性
- class 构造函数在它们的实例类型中是协变的
Contravariance表示F<T>
和T
contra-vary.也就是说,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
}
其他(暂时未显示)示例是:
- 对象的键类型是逆变的
- class 构造函数在它们的构造参数类型中是逆变的
不变性意味着F<T>
既不随T
变化也不反对T
:F<T>
在T
中既不协变也不逆变.这实际上是最一般情况下发生的情况。协变和逆变是“脆弱的”,因为当您组合协变和逆变类型函数时,很容易产生不变的结果。示例:
return与其参数类型相同的函数类型在该类型中既不共变也不对变:
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!
}
Bivariance 意味着 F<T>
变化 both with and against 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 没有 nor does it intend to have a fully sound type system. And there is one notable case 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
}
能否使用简单的 TypeScript 示例解释什么是方差、协方差、逆变和双方差?
[持续更新]
有用链接:
Another one good answer of Oleg Valter与主题相关
Very good explanation of*-riance by Titian-Cernicova-Dragomir
Scala documentation - 很好的解释和例子
@提香的
@提香的
@jcalz
Variance 与泛型类型 F<T>
varies 相对于其类型参数 T
.如果你知道 T extends U
,那么方差会告诉你是否可以得出 F<T> extends F<U>
,得出 F<U> extends F<T>
,或者两者都不能得出。
Covariance 表示 F<T>
和 T
co-vary.也就是说,F<T>
随 变化(与 T
的方向相同)。换句话说,如果T extends U
,那么F<T> extends F<U>
。示例:
函数或方法类型与其 return 类型共同变化:
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! }
其他(暂时未显示)示例是:
- 对象在它们的 属性 类型中是协变的,即使这听起来不适合可变属性
- class 构造函数在它们的实例类型中是协变的
Contravariance表示F<T>
和T
contra-vary.也就是说,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 }
其他(暂时未显示)示例是:
- 对象的键类型是逆变的
- class 构造函数在它们的构造参数类型中是逆变的
不变性意味着F<T>
既不随T
变化也不反对T
:F<T>
在T
中既不协变也不逆变.这实际上是最一般情况下发生的情况。协变和逆变是“脆弱的”,因为当您组合协变和逆变类型函数时,很容易产生不变的结果。示例:
return与其参数类型相同的函数类型在该类型中既不共变也不对变:
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! }
Bivariance 意味着 F<T>
变化 both with and against 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 没有 nor does it intend to have a fully sound type system. And there is one notable case 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 }