TypeScript 交集和并集取决于类型变量示例化顺序,

kpbwa7wx  于 6个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(64)

🔎 搜索词

通用类型变量交集并集顺序

🕗 版本与回归信息

  • 这是我尝试的每个版本的行为,我查阅了关于泛型的常见问题解答

⏯ Playground链接

工作台复现

💻 代码

type Union<A, B> = A | B
type UnionAny<T> = Union<T, any>
type UnionUnknown<T> = Union<unknown, T>

// these should all be the same type but are respectively: any, any, unknown 
type UA0 = Union<unknown, any>
//   ^?
type UA1 = UnionAny<unknown>
//   ^?
type UA2 = UnionUnknown<any>
//   ^?

type Intersect<A, B> = A & B
type IntersectAny<T> = Intersect<T, any>
type IntersectNever<T> = Intersect<never, T>

// these should all be the same type but are respectively: never, any, never
type AN0 = Intersect<never, any>
//   ^?
type AN1 = IntersectAny<never>
//   ^?
type AN2 = IntersectNever<any>
//   ^?

🙁 实际行为

类型 UA0 , UA1 分别解析为 anyUA2 解析为 unknown 。它们都Map到相同的 unknownany 的联合类型,所以应该是相同的类型。
类型 AN0 , AN2 分别解析为 neverAN1 解析为 any 。它们都Map到 anynever 的交集类型,所以应该是相同的类型。

🙂 预期行为

我期望 UA0 , UA1UA2 能够解析为相同的类型(可能是 any ,但最好是 unknown )
我期望 AN0 , AN1AN2 能够解析为相同的类型(可能是 never )

关于问题的附加信息

  • 无响应*
daupos2t

daupos2t1#

它只在左侧是类型参数时才分配。
具体来说,一个裸类型参数。像 (T & U) extends ... 这样的东西也不是分配的。
噢。你是对的。我无法确定这是 bug 还是特性。
例如,T|T 的行为类似于裸类型参数 T,但 T|U 并不这样,即使当 TU 用相同的类型示例化时也是如此!
还有一个奇怪的情况是,即使 LHS 不包含类型参数,当 LHS 是 any 但 RHS 不是时,条件表达式会计算出两个分支的并集。

pkbketx9

pkbketx92#

还有一种奇怪的情况,即使左侧不包含类型参数,当左侧是除右侧之外的任何值时,条件表达式的结果为两个分支的并集。
是的,这是与分布分开的行为,是有意图的。

sq1bmfud

sq1bmfud3#

还有一个有趣的情况是,即使左侧不包含类型参数,当左侧是除右侧之外的任何值时,条件表达式的结果为两个分支的并集。
是的,这是与分布分开的行为,并且是有意为之的。
是的,这是有意为之的。原因(anyunion 的上界,所以我们应该推断出尽可能通用的类型)似乎暗示 unknown extends T ? A : B 应该计算为 A | B

liwlm1x9

liwlm1x94#

问题可能是unknown被假定为|any的吸收元素,而&被假定为一个吸收元素。我认为包含类型参数的类型表达式应该只依赖于这些类型参数的最终值是合理的。

yzuktlbb

yzuktlbb5#

一般来说,联合和交集类型的操作符都是可交换的(函数交集除外,它们被视为重载,并且是顺序相关的),任何包含 any 的联合/交集都应该始终简化为 any

lokaqttq

lokaqttq6#

@fatcerberus 抓住了关键点。
包含任何的联合/交集总是应该简化为任何。
我认为以下两种情况在逻辑上都是一致的:

  • any | unknownany & never 都是 any,因为 any 代表了类型系统的幂等性。
  • T | unknownunknown,T & nevernever,因为 unknownnever 限制了类型层次结构,而 any 不包含关于限定值的信息。

为什么前者应该被覆盖?我找不到明确说明这一点的文档,但这感觉是一个重要的设计选择。
编辑:我可以看到意图是在 #24439 中让 unknown & anyunknown | any 都成为 any。但是那里记录的行为与当前行为不同,即:

type T32<T> = never extends T ? true : false;  // true

这似乎比当前行为(在这种情况下,这种类型评估为 never)更一致。
再次编辑:我对分配条件的理解是错误的。它只在左侧是类型参数时分配。

2izufjch

2izufjch7#

它仅在左侧为类型参数时才分配。
具体来说,一个裸露的类型参数。像 (T & U) extends ... 或类似的东西也不是可分配的。这就是为什么你可以写 [T] extends [U] ? ... 以防止分配的原因。

相关问题