我试图理解 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 的 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