🔎 搜索词
class parameter generic bivariant constructor typeof extends
🕗 版本与回归信息
- 这是我在每个版本中尝试的行为
⏯ Playground链接
https://www.typescriptlang.org/play?ts=5.3.0-dev.20231026#code/C4TwDgpgBAggRgZ2AJwIYGNirgGwgYQHsA7JZAV00OQB4BLUrY9aAXigG8BfAPinexkMwKMQgB3KAAoAsACgAkADoVqZAHMEALlEQAbhGQBtALryAlPz4MkqZhHnzQkKAElGdlgHkAZjXQkZJTA1Hys8goBjBRUyFAQAB7AEMQAJgiwiCjC2HhE0cHU9MQ+hlA2TCx8APzlHvZQOmIGyI5y6DioCBkAYoSEnBGpEB1q0HBqOijkDnJcbaPdUAAKyIQhNGBrIfFJKemZQpi5BIHTsfyH2ce4pwWxfBxDI53I0BWeEDrutva+m9tCDx5PM5PIAPTgqBeADWTnA0Bgl1W60IRgARB97OizHJIVACQA9aptfGw+EuABCyMBNGcEEIPigfSBGKxLBxEKhRJJYLxUPJcnpUHwlyRiWSaQy1Nq02gOh8qBwCFm+J5pKhABUABZ0DIIbWEcg4VJQODQRXK2bCgBaNNRu0lBxRG3pjOZ-RqUDljSglpVXIJUGJQA
💻 代码
type AbstractableConstructor<instance = {}> = abstract new (
...args: never[]
) => instance
type InstanceOf<constructor> =
constructor extends AbstractableConstructor<infer instance> ? instance : never
class Foo {
declare bar: true
}
class Proto<proto extends AbstractableConstructor = AbstractableConstructor> {
declare instance: InstanceOf<proto>
}
// Ok
type A = Proto["instance"]
// ^? {}
// Ok
type B = Proto<typeof Foo>["instance"]
// ^? Foo
// Ok
type C = A extends B ? true : false
// ^? false
// This should be false
type Z = Proto extends Proto<typeof Foo> ? true : false
// ^? true
🙁 实际行为
proto
被当作bivariant处理,没有结构比较,导致 Z
评估为 true
。
🙂 预期行为
proto
应该被视为协变,因为只使用其返回类型或结构上进行比较,导致 Z
评估为 false
。
关于问题的附加信息
- 无响应*
9条答案
按热度按时间qybjjes11#
相当奇怪。幸好,通过在
proto
上添加一个out
注解,可以轻松解决这个问题。whitzsjs2#
这里的问题是,方差计算将
proto
类型参数归类为独立(即未被观察到,除了那些扩展了AbstractableConstructor
的类型之外)。不清楚我们是否能在这里做得更好,毕竟proto
唯一的用途是在InstanceOf
中作为推理的来源,我们无法测量这种条件的方差。bt1cpqcv3#
正如@RyanCavanaugh所说,一旦你知道这是发生的事情,解决方法就很简单了,但要达到这一点?在现实世界的情况下可能就不那么简单了。
@Andarist提到这可能与可变性“无法衡量”有关。如果不能准确推断,我宁愿在允许这样的赋值时明确地进行类型转换,而不是在不需要时得到一个错误的阳性结果。
在这种情况下,有没有一种方法可以不破坏大量现有代码的情况下将参数视为不变而不是双变?
bweufnob4#
我们可能会考虑在测量变异时使用某种诊断方法,但这通常不是有意为之的。然而,毫无疑问,这将是一个破坏性的改变,并且需要在(另一个)编译器开关下进行。
或者,这可以是一个lint问题,只要我们的API中暴露了必要的类型检查器信息。
xmjla07d5#
实际上,我们测量
InstanceOf
为双变性,因此Proto
也会测量双变性。双变性来自于规则的影响:似乎有点任意地选择双变性,因为我们没有使用用于方差探测的标记类型来衡量结果之间的差异。实际上,我们是在说,当我们无法测量条件类型的方差时,我们认为该条件类型是双变性的。
czfnxgou6#
在这里,
InstanceOf
被正确地视为协变。but5z9lq7#
InstanceOf
在这里被正确地视为协变。是的,这里发生的是
InstanceOf<AbstractableConstructor>
和InstanceOf<typeof Foo>
在条件类型被评估之前被解析为{}
和Foo
。所以,这就像写{} extends Foo ? true : false
一样,在那个时候我们甚至不参考计算出的InstanceOf
的变异性。但是在涉及两个
Proto
示例的情况下,我们确实会参考变异性信息,并且,由于它声称双变性,我们认为Proto<AbstractableConstructor>
和Proto<typeof Foo>
在两个方向上都相关。你可以通过强制结构比较来看到区别:理想情况下,我们应该检测到条件类型变异性无法测量的情况,并强制进行结构比较,但我们在过去尝试过,结果对性能影响非常糟糕。
92dk7w1h8#
我发现这个有点难以理解,所以整理了一个更简单的复现。我想在这里发布它,以防它对其他人有帮助:
zzwlnbp89#
另一种解决方法,不需要手动差异注解,是将内联类型表达式提取到一个新的类型变量中。
或者,在简化的例子中:
也就是说,类型相对于
proto
/B
是有条件的独立的,给定T
。这似乎比结构比较便宜得多,而且非常简单,可以自动完成。