Variance涉及到泛型类型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>
。例如:
Function or method types co-vary with their return types:
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>
。例如:
Function types contra-vary with their parameter types (with --strictFunctionTypes
enabled):
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
变化也不反对T
:在T
中,F<T>
既不是协变的也不是逆变的。实际上,在最一般的情况下,这就是发生的情况。协变和逆变是“脆弱”的,因为当你结合协变和逆变类型函数时,很容易产生不变的结果。例如:
Function types that return the same type as their parameter neither co-vary nor contra-vary in that type:
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>
在 T
上同时是协变和逆变的: F<T>
在 T
上既不是协变也不是逆变。在一个良好的类型系统中,对于任何非平凡的类型函数,这基本上是不可能发生的。您可以证明只有一个常量类型函数,例如 type F<T> = string
真正是双变量的(快速草图:对于所有的 T
,T extends unknown
都成立,因此 F<T> extends F<unknown>
且 F<unknown> extends T
,在一个良好的类型系统中,如果 A extends B
和 B extends A
,那么 A
就等同于 B
。因此,如果对于所有的 T
,F<T>
= F<unknown>
,则 F<T>
是常量)。Method types both co-vary and contra-vary with their parameter types (this also happens with all function types with --strictFunctionTypes
disabled):
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
}
A.S.:jcalz的回答从技术角度来看非常好。我想补充一些直观的解释。
当你处理两种类型既不完全相同,也不完全无关的情况时,方差的概念变得有用。
原因如下。
在这个例子中,value1
和value2
都是相同类型的number
,因此一个总是可以赋值给另一个,反之亦然,没有任何错误:
declare let value1: number // for example: 42, or -3, or NaN, etc.
declare let value2: number // for example: 17, or Math.PI, or Infinity, etc.
value1 = value2 // no error
value2 = value1 // no error
value1
是一个number
,而value2
是一个string
。这些类型彼此之间没有任何关联,因此将一个赋值给另一个总是错误的。declare let value1: number // for example: 42, or 17, etc.
declare let value2: string // for example: "Hello world", "Lorem ipsum", etc.
value1 = value2 // Error!
value2 = value1 // Error!
value2
到value1
还是相反),结果总是一样的:第一种情况总是没有错误,而第二种情况总是有错误。这就是为什么这里没有涉及到变异性。Person
只有一个属性name
,而类型Student
定义了name
和graduationYear
两个属性。也就是说,Student
包含了Person
的所有内容,而Person
只部分涵盖了Student
。将一个人分配给一个学生是错误的,因为一个学生应该有一个毕业年份,但Person
只有name
属性。然而,将一个学生分配给一个人是完全可以的,因为一个学生,就像任何一个人一样,都有一个名字(无论是否有其他属性)。type Person = { name: string }
type Student = { name: string, graduationYear: number }
declare let person: Person // for example: { name: 'Mike' }
declare let student: Student // for example: { name: 'Sofia', graduationYear: 2020 }
person = student // no error
student = person // Error!
…
好的,让我们来解开这个问题。Array
:即使两者都是数组,Array<number>
和Array<string>
并不相同。在Array<number>
中的number
和Array<string>
中的string
是泛型类型Array<…>
的类型参数(或类型参数)。Array
对其类型参数没有任何约束;对于任何Value
,表达式Array<Value>
都创建了一个完全有效且可用的类型。问题是,如果我有Array<A>
和Array<B>
,它们可以互相赋值吗?将此与血型进行比较可能会有所帮助(尽管可能有点可怕):A型含有抗B抗体;B型含有抗A抗体;AB型不含抗体;最后,O型含有两种抗体。