代码之家  ›  专栏  ›  技术社区  ›  captain-yossarian from Ukraine

TypeScript中方差、协方差、对方差和双方差的差异

  •  1
  • captain-yossarian from Ukraine  · 技术社区  · 4 年前

    你能用简单的TypeScript例子解释一下什么是方差、协方差、逆变和双方差吗?

    0 回复  |  直到 2 年前
        1
  •  40
  •   Qwerty    2 年前

    方差 与泛型类型的方式有关 F<T> 变化 关于其类型参数 T 如果你知道的话 T extends U ,那么方差将告诉你是否可以得出以下结论 F<T> extends F<U> ,得出以下结论 F<U> extends F<T> 或者两者都没有,或者两者都有。


    协方差 意思是 F<T> T 有限公司 - 变化 也就是说, F<T> (方向与) T 换句话说,如果 T延伸U 那么 F<T>扩展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延伸U 那么 F<U>扩展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> 变化 二者都 具有 反对 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 A 那么 A B 那么,如果 F<T> = F<unknown> 为所有人 T 那么 F<T> 恒定)。

    但Typescript没有 nor does it intend to have 全声音型系统。还有 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
      }
      

    Playground link to code