TypeScript中的方差、协方差、逆变和双方差的区别

您能否使用小而简单的 TypeScript 示例来解释什么是方差、协方差、逆变和双方差?

[持续更新]

有用的链接:

  1. Oleg Valter 的另一个与该主题相关的好答案

  2. Titian-Cernicova-Dragomir对*-rianance 的很好解释

  3. 斯蒂芬博耶博客

  4. Scala 文档- 用例子很好的解释

  5. @Titian 的回答 1

  6. @Titian 的回答 2

  7. Vlad Riscutia 的博客

  8. 马克西曼的文章

回答

差异有一个泛型类型如何做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>既不随 也不随 变化TF<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!
    }
    

Bivariance意味着F<T>变化反对TF<T>既是协也不逆变在T。在健全的类型系统中,对于任何非平凡的类型函数,这基本上不会发生。您可以证明只有像这样的常量类型函数type F<T> = string才是真正的二元函数(快速草图:T extends unknown对 all T、soF<T> extends F<unknown>和都是真实的F<unknown> extends T,并且在声音类型系统中 if A extends Band B extends B, thenA与 相同B。所以如果F<T>= F<unknown>for all 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
    }
    

Playground 链接到代码


以上是TypeScript中的方差、协方差、逆变和双方差的区别的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>