为什么 TypeScript 拒绝 [number] 和 Array<1> 但接受其他类似的交集?

问题描述 投票:0回答:1

我试图理解 TypeScript 如何处理元组和数组类型之间的交集的明显不一致。这是代码:

const a: [number] & Array<1> = [1]; // Error
const b: [1 | 2] & Array<1> = [1]; // OK
const c: Array<number> & Array<1> = [1]; // OK
const d: [number] & [1] = [1]; // OK

a
的错误消息是:

Type '[number]' is not assignable to type '[number] & 1[]'.
  Type '[number]' is not assignable to type '1[]'.
    Type 'number' is not assignable to type '1'.

为什么 TypeScript 拒绝

a
但接受其他情况?这是由于 TypeScript 类型系统中有关元组和数组交集的某些特定规则所致吗?

typescript
1个回答
0
投票

警告:这个答案是推测性的;为了获得权威答案,我要么必须在 TypeScript 的 GitHub 存储库中发布问题,因为我无法找到解释这一特定行为的现有问题;要么或者单步执行 TypeScript 编译器代码以了解发生了什么。我现在还不准备做这两件事。如果有人这样做并发布了权威答案,我很乐意编辑或删除它。

另请注意:数组类型的交集在 TypeScript 中至多表现不佳。有关更多信息,请参阅 microsoft/TypeScript#41874 以及其中链接的问题。使用这些类型来探测奇怪的 TypeScript 行为固然很好,但如果您在生产代码中发现它们,您应该尝试删除它们、解决它们、远离它们等等。


TypeScript 需要推断 数组文字

[1]
的类型。在没有任何上下文的情况下,它会将其扩展为
number[]
,因为启发式地,这就是人们倾向于从数组文字中期望的:

const z = [1];
//    ^? const z: number[]

通过使用预期类型注释变量,您可以提供一个上下文,TypeScript 应在其中推断数组文字的类型。通常,当上下文包含tuples时,TypeScript 将尝试从数组文字推断元组类型。一般来说,当上下文包含数字文字类型(如

1
)时,TypeScript 将尝试从数字文字值推断文字类型,而不仅仅是
number
:

const y: [1 | 7, string?] = [1]; // okay
const x: [2 | 7, string?] = [1]; // error!
// Type '1' is not assignable to type '7 | 2'.

最上面的一个成功,因为值

[1]
被推断为具有类型
[1]
,因为
[1 | 7, string?]
的上下文暗示具有数字文字元素的元组,并且
[1]
可分配给
[1 | 7, string?]
。 底部的失败是因为
[1]
被推断为具有
[1]
类型,因为
[2 | 7, string?]
的上下文暗示具有数字文字元素的元组,但
[1]
不能 分配给
[2 | 7, string?]


您看到的问题与 TypeScript 如何决定上下文类型如何影响数组文字的推理的一些细节有关。我的猜测是,当上下文包含两者元组非元组数组类型时,TypeScript 会认为元组类型更具体,并且仅将其用于上下文推理。因此,面对

const x: [A] & B[] = [⋯]
const x: B[] & [A] = [⋯]
,您可能会看到
被推断为
A
,而不是
B
。这与您所看到的一致:

const a: [number] & Array<1> = [1]; // inferred as [number], fails
const b: [1 | 2] & Array<1> = [1]; // inferred as [1], succeeds
const c: Array<number> & Array<1> = [1]; // inferred as 1[], succeeds
const d: [number] & [1] = [1]; // inferred as [1], succeeds

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.