AbstractRecoilValue<T> 上的 'contravariance' 在 Recoil 源类型声明中意味着什么?
What does 'contravariance' on AbstractRecoilValue<T> mean in Recoil source type declarations?
我在 Recoil 来源中偶然发现了 typescript/index.d.ts 的这一部分:
export class AbstractRecoilValue<T> {
__tag: [T];
__cTag: (t: T) => void; // for contravariance
key: NodeKey;
constructor(newKey: NodeKey);
}
这里__cTag
如何作为逆变的判别器?
这是整个上下文:Github
我认为“for contravariance”可能应该改为“to prevent covariance”,但无论如何我都会解释发生了什么。假设您有一个类型函数 type F<T> = ...
。如果您有两种类型 A
和 B
,其中 B extends A
,例如
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
。它“共同变化”,或者说是协变的。函数类型在它们的 return 类型中是协变的。在 TypeScript 中,对象类型在其 属性 类型中被认为是协变的(尽管这是 , it is useful). Array<T>
is covariant in T
(even though this is ,但它很有用)。因此 [T]
在 T
.
中是协变的
F<T>
在 T
中是逆变的:只要 B extends A
,那么 F<A> extends F<B>
。由于扩大 T
会缩小 F<T>
,缩小 T
会扩大 F<T>
,因此可以说 F<T>
与 [=130= 相反] T
。它“反变化”,或者说是逆变的。函数类型(the --strictFunctionTypes
compiler option enabled)的参数类型是逆变的。所以 (t: T) => void
在 T
中是逆变的。在 TypeScript 中,对象类型在其 属性 键类型中也被认为是逆变的。
在上面的例子中,当我们说“F<T>
在 T
中是协变的”时,我们也意味着它是 不是 逆变的。反之亦然;当我们说“F<T>
在 T
中是逆变的”时,我们也意味着它是 而非 协变的。所以 [T]
在 T
中是协变的(但不是逆变的),而 (t: T) => void
在 T
中是逆变的(但不是协变的)。但是我们也可以考虑这种情况:
F<T>
在 T 中是双变的:F<T>
在 T
中既是协变又是逆变的。在完全健全的类型系统中,除非 F<T>
根本不依赖于 T
,否则不会发生这种情况。但在 TypeScript 中,method types (or all function types with --strictFunctionTypes
disabled) are considered bivariant in their parameter types。另一种无用的情况
最后:
F<T>
在T
中不变:F<T>
在T
中既不协变也不逆变。当 B extends A
时,F<A>
和 F<B>
之间没有简单的关系。这往往是最常见的情况。协变和逆变在某种意义上是“脆弱的”,那么如果你组合协变和逆变类型,你往往会得到不变类型。这就是在 AbstractRecoilValue<T>
定义中组合 [T]
和 (t: T) => void
时发生的情况。 _tag
属性 在 T
中是协变的(但不是逆变的),而 _cTag
属性 在 T
中是逆变的(但不是协变的) .通过将它们放在一起,AbstractRecoilValue<T>
在 T
.
中是不变的
所以大概添加了 _cTag
,以便 AbstractRecoilValue<T>
在 T
中不变,在 T
中不协变。如果将其注释掉,您可以看到行为上的差异:
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
:
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>
进行这样的限制,我无法回答,因为我不确定它的用途...不过,这似乎超出了问题的范围。
我在 Recoil 来源中偶然发现了 typescript/index.d.ts 的这一部分:
export class AbstractRecoilValue<T> {
__tag: [T];
__cTag: (t: T) => void; // for contravariance
key: NodeKey;
constructor(newKey: NodeKey);
}
这里__cTag
如何作为逆变的判别器?
这是整个上下文:Github
我认为“for contravariance”可能应该改为“to prevent covariance”,但无论如何我都会解释发生了什么。假设您有一个类型函数 type F<T> = ...
。如果您有两种类型 A
和 B
,其中 B extends A
,例如
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
。它“共同变化”,或者说是协变的。函数类型在它们的 return 类型中是协变的。在 TypeScript 中,对象类型在其 属性 类型中被认为是协变的(尽管这是 Array<T>
is covariant in T
(even though this is [T]
在 T
.
F<T>
在 T
中是逆变的:只要 B extends A
,那么 F<A> extends F<B>
。由于扩大 T
会缩小 F<T>
,缩小 T
会扩大 F<T>
,因此可以说 F<T>
与 [=130= 相反] T
。它“反变化”,或者说是逆变的。函数类型(the --strictFunctionTypes
compiler option enabled)的参数类型是逆变的。所以 (t: T) => void
在 T
中是逆变的。在 TypeScript 中,对象类型在其 属性 键类型中也被认为是逆变的。
在上面的例子中,当我们说“F<T>
在 T
中是协变的”时,我们也意味着它是 不是 逆变的。反之亦然;当我们说“F<T>
在 T
中是逆变的”时,我们也意味着它是 而非 协变的。所以 [T]
在 T
中是协变的(但不是逆变的),而 (t: T) => void
在 T
中是逆变的(但不是协变的)。但是我们也可以考虑这种情况:
F<T>
在 T 中是双变的:F<T>
在 T
中既是协变又是逆变的。在完全健全的类型系统中,除非 F<T>
根本不依赖于 T
,否则不会发生这种情况。但在 TypeScript 中,method types (or all function types with --strictFunctionTypes
disabled) are considered bivariant in their parameter types。另一种无用的情况
最后:
F<T>
在T
中不变:F<T>
在T
中既不协变也不逆变。当 B extends A
时,F<A>
和 F<B>
之间没有简单的关系。这往往是最常见的情况。协变和逆变在某种意义上是“脆弱的”,那么如果你组合协变和逆变类型,你往往会得到不变类型。这就是在 AbstractRecoilValue<T>
定义中组合 [T]
和 (t: T) => void
时发生的情况。 _tag
属性 在 T
中是协变的(但不是逆变的),而 _cTag
属性 在 T
中是逆变的(但不是协变的) .通过将它们放在一起,AbstractRecoilValue<T>
在 T
.
所以大概添加了 _cTag
,以便 AbstractRecoilValue<T>
在 T
中不变,在 T
中不协变。如果将其注释掉,您可以看到行为上的差异:
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
:
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>
进行这样的限制,我无法回答,因为我不确定它的用途...不过,这似乎超出了问题的范围。