首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在反冲源类型声明中,AbstractRecoilValue<T>上的“反向方差”意味着什么?

在反冲源类型声明中,AbstractRecoilValue<T>上的“反向方差”意味着什么?
EN

Stack Overflow用户
提问于 2021-08-17 13:01:41
回答 1查看 159关注 0票数 4

我在反冲源中偶然发现了这部分类型记录/index.d.ts:

代码语言:javascript
复制
export class AbstractRecoilValue<T> {
    __tag: [T];
    __cTag: (t: T) => void; // for contravariance

    key: NodeKey;
    constructor(newKey: NodeKey);
}

在这里,__cTag是如何作为反方差的鉴别器的?

以下是完整的上下文:Github

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-08-17 19:49:01

我认为“反方差”应该改为“防止协方差”,但我会解释在任何情况下发生了什么。假设您有一个类型函数type F<T> = ...。如果有两种类型的AB,其中B extends A,如

代码语言:javascript
复制
interface A { a: string }
interface B extends A { b: number }

declare let a: A;
declare let b: B;
a = b; // okay
b = a; // error

关于F<A>F<B>之间的关系,你能说些什么呢?有两个令人感兴趣的案例需要考虑:

F<T> T:中是协变的,无论何时B extends A,然后是F<B> extends F<A>。由于扩大T将扩大F<T>,而缩小T将缩小F<T>,因此可以说F<T>T是不同的。它是“共变”的,或者是协变的。函数类型在其返回类型中是协变量的。在TypeScript中,对象类型在其属性类型中被认为是协变的(即使这是写作时声音不佳,它也是有用的)。Array<T>T中是协变的(即使这是写作时声音不佳,它还是有用的)。所以[T]T中是协变的。

F<T> T:中是相反的,无论何时B extends A,然后是F<A> extends F<B>。由于扩大T将缩小F<T>,而缩小T将扩大F<T>,因此可以说F<T>T相反。它“反变”,或者说是反变体。函数类型(带有编译器选项已启用)在它们的参数类型中是相反的。因此,(t: T) => voidT中是逆变的。在TypeScript中,对象类型在其属性键类型中也被认为是反变体。

在上面的例子中,当我们说"F<T>T中是协变的“时,我们也意味着它不是反变体。反之亦然;当我们说"F<T>T中是反变体“时,我们也意味着它不是协变的。因此,[T]T中是协变的(但不是反变的),(t: T) => voidT中是反变的(但不是协变的)。但我们也可以考虑这个案子:

F<T> 在T中是双变异的:F<T>T中既是协变也是反变体。在完全健全的类型系统中,除非F<T>完全不依赖T,否则不会发生这种情况。但在TypeScript,)在其参数类型中被认为是双变量。。另一种不可靠但有用的情况。

最后:

F<T> 中是不变量的,F<T>T中既不是协变,也不是反变。当F<A>F<B>之间没有简单的关系时,B extends A。这往往是最常见的情况。协方差和反方差在这个意义上是“脆弱的”,那么如果你组合协变和反变类型,你就会得到不变的类型。当您将[T](t: T) => void组合到AbstractRecoilValue<T>定义中时,就会发生这种情况。_tag属性在T中是协变量(但不是反变体),_cTag属性在T中是反变体(而不是协变量)。通过将它们组合在一起,AbstractRecoilValue<T>T中是不变的。

因此,想必添加了_cTag,所以AbstractRecoilValue<T>T中是不变的,而在T中不是协变的。如果您将其注释掉,您可以看到行为上的差异:

代码语言:javascript
复制
declare class AbstractRecoilValue<T> {
  __tag: [T];
  // __cTag: (t: T) => void; // for contravariance
  key: NodeKey;
  constructor(newKey: NodeKey);
}

declare let arvA: AbstractRecoilValue<A>;
declare let arvB: AbstractRecoilValue<B>;

arvA = arvB; // okay
arvB = arvA; // error!

您可以看到AbstractRecoilValue<B>可以分配给AbstractRecoilValue<A>,但反之亦然。因此,AbstractRecoilValue<T>T中是协变的。但是如果我们恢复__cTag

代码语言:javascript
复制
declare class AbstractRecoilValue<T> {
  __tag: [T];
  __cTag: (t: T) => void; // for contravariance
  key: NodeKey;
  constructor(newKey: NodeKey);
}

declare let arvA: AbstractRecoilValue<A>;
declare let arvB: AbstractRecoilValue<B>;

arvA = arvB; // error!
arvB = arvA; // error!

因此,AbstractRecoilValue<T>T中是不变的。

如果你想知道为什么对AbstractRecoilValue<T>设置这样的限制,我不能回答,因为我不知道它是用来做什么的.不过,这似乎超出了所提问题的范围。

操场链接到代码

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68817839

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档