TypeScript 中的方差、协方差、逆变和双方差的区别 [英] Difference between Variance, Covaraince, Contravariance and Bivariance in TypeScript

查看:31
本文介绍了TypeScript 中的方差、协方差、逆变和双方差的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

[持续更新]

有用的链接:

  1. 另一个Oleg Valter 与主题相关

  2. 很好的解释*-riance by Titian-Cernicova-Dragomir

  3. Stephan Boyer 博客

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

  5. @Titian 的答案 1

  6. @Titian 的答案 2

  7. Vlad Riscutia 的博客

  8. Mark Seemann 的文章>

解决方案

Variance 与泛型类型 F<T> varies 的变化有关em> 关于它的类型参数 T.如果您知道 T 扩展 U,那么方差将告诉您是否可以得出 F 的结论.扩展 F,得出 F;扩展 F<T>,或者两者都不扩展,或者两者都扩展.


Covariance 表示 FT co-vary.也就是说,F 变化(与T同向).换句话说,如果 T 扩展 U,则 F;扩展 F.示例:

  • 函数或方法类型与其返回类型共同变化:

    类型 Co= () =>Ⅴ;函数协方差(t:T,u:U,coT:Co T ,coU:Co U ){u = t;//好的t = u;//错误!coU = coT;//好的coT = coU;//错误!}

其他(暂时未说明)示例是:

  • 对象在它们的属性类型上是协变的,即使这对于可变属性来说不合理
  • 类构造函数的实例类型是协变的

逆变意味着FT相反-变化.也就是说,F 相反(与T相反).换句话说,如果 T 扩展 U,则 F扩展 F.示例:

  • 函数类型与其参数类型相反(使用 --strictFunctionTypes 已启用):

    type Contra= (v: V) =>空白;函数逆变(t:T,u:U,contraT:Contra,contraU:Contra){u = t;//好的t = u;//错误!contraU = contraT;//错误!contraT = contraU;//好的}

其他(暂时未说明)示例是:

  • 对象的键类型是逆变的
  • 类构造函数的构造参数类型是逆变的

Invariance 意味着 F 既不随 T 变化也不随 T 变化:FT 中既不协变也不逆变.这实际上是最一般情况下发生的情况.协方差和逆变是脆弱的"因为当你结合协变和逆变类型函数时,很容易产生不变的结果.示例:

  • 返回与其参数相同类型的函数类型在该类型中既不共变也不反变:

    输入= (v: V) =>Ⅴ;函数不变性(t:T,u:U,inT:In,inU:In){u = t;//好的t = u;//错误!inU = inT;//错误!inT = inU;//错误!}


Bivariance 意味着 F both 相对于 T 变化>: FT 中既是协变又是逆变.在健全的类型系统中,对于任何非平凡的类型函数,这基本上永远不会发生.您可以证明只有像 type F<T> 这样的常量类型函数才可以.= string 确实是双变的(速写:T extends unknown 对所有 T 都是正确的,所以 F extends FF extends T,在声音类型系统中,如果 A extends BB extends B,则 AB 相同.所以如果 F = F 对于所有 T,则 F 是常数).

但是 Typescript 没有 它也不打算有 一个完全健全的类型系统.还有一个一个值得注意的案例,其中TypeScript对待作为双变量的类型函数:

  • 方法类型与其参数类型同时变化和相反(这也发生在所有禁用 --strictFunctionTypes 的函数类型中):

    type Bi= { foo(v: V): void };函数双方差<U,T扩展U>(t:T,u:U,biT:Bi T ,biU:Bi U ){u = t;//好的t = u;//错误!双 = 比特;//好的双 = 双;//好的}

游乐场代码链接

Could you please explain using small and simple TypeScript examples what is Variance, Covariance, Contravariance and Bivariance?

[CONTINUOUS UPDATE]

Useful links:

  1. Another one good answer of Oleg Valter related to the topic

  2. Very good explanation of*-riance by Titian-Cernicova-Dragomir

  3. Stephan Boyer blog

  4. Scala documentation - good explanation with examples

  5. @Titian's answer 1

  6. @Titian's answer 2

  7. Vlad Riscutia 's blog

  8. Mark Seemann 's article

解决方案

Variance has to do with how a generic type F<T> varies with respect to its type parameter T. If you know that T extends U, then variance will tell you whether you can conclude that F<T> extends F<U>, conclude that F<U> extends F<T>, or neither, or both.


Covariance means that F<T> and T co-vary. That is, F<T> varies with (in the same direction as) T. In other words, if T extends U, then F<T> extends F<U>. Example:

  • 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!
    }
    

Other (un-illustrated for now) examples are:

  • objects are covariant in their property types, even though this not sound for mutable properties
  • class constructors are covariant in their instance types

Contravariance means that F<T> and T contra-vary. That is, F<T> varies counter to (in the opposite direction from) T. In other words, if T extends U, then F<U> extends F<T>. Example:

  • 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
    }
    

Other (un-illustrated for now) examples are:

  • objects are contravariant in their key types
  • class constructors are contravariant in their construct parameter types

Invariance means that F<T> neither varies with nor against T: F<T> is neither covariant nor contravariant in T. This is actually what happens in the most general case. Covariance and contravariance are "fragile" in that when you combine covariant and contravariant type functions, its easy to produce invariant results. Example:

  • 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!
    }
    


Bivariance means that F<T> varies both with and against T: F<T> is both covariant nor contravariant in T. In a sound type system, this essentially never happens for any non-trivial type function. You can demonstrate that only a constant type function like type F<T> = string is truly bivariant (quick sketch: T extends unknown is true for all T, so F<T> extends F<unknown> and F<unknown> extends T, and in a sound type system if A extends B and B extends B, then A is the same as B. So if F<T> = F<unknown> for all T, then F<T> is constant).

But Typescript does not have nor does it intend to have a fully sound type system. And there is one notable case where TypeScript treats a type function as bivariant:

  • 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
    }
    

Playground link to code

这篇关于TypeScript 中的方差、协方差、逆变和双方差的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆