`string & any[]` 不应该导致 `never` 吗?

m93*_*93a 1 typescript

我注意到 TypeScript 有一些奇怪的地方。我有一个类型联合,其中包含一些数组类型(string[], number[])和一些非数组类型(string, number)。如果我使用类型推断,一切都会按预期进行:

type bar = string | number | string[] | number[];
declare foo: bar;

if (Array.isArray(foo))
{
    foo // string[] | number[]
}
else
{
    foo // string | number
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我想将类型直接限制为数组类型并使用类型交集,我会得到一些意想不到的结果:

declare foo: bar & any[];

// expected type: string[] | number[]

foo // (string & any[]) | (number & any[]) | (string[] & any[]) | (number[] & any[])
Run Code Online (Sandbox Code Playgroud)

这是为什么?
不应该string & any[]评估 toneverstring[] & any[]tostring[]吗?

[游乐场链接]

jca*_*alz 5

考虑到never两个不重叠集合的交集为空的直觉,完全不相交类型的交集应计算为 是合理的。此前曾多次提出过这一要求。

事实上,自 TypeScript 2.6 以来,这种减少never已经部分实现。具体来说,像这样的类型("a" | "b") & "c"会变成never,而"a" & "c"不会。这样做是为了合并并集和交集不会导致巨大的并集类型。

但是引入此实现的拉取请求的描述可以让您深入了解您的问题的答案:“为什么编译器不一直这样做”?下面的引述来自Anders Hejlsberg,他是 TypeScript 语言的首席维护者/架构师之一。

他提到了一个问题:

理论上,我们可以更积极地删除空交集类型,但我们不想破坏使用交集“标记”基本类型的代码。

这种“标记”或“品牌”是一种在 TypeScript 中模拟名义输入的方法。TypeScript 使用结构类型来比较类型,这意味着编译器不会区分两种类型A以及B它们是否具有相同的形状。有时,您希望编译器能够以不同的方式处理两个完全相同的类型(这是名义类型语言(如 Java)的默认行为,其中仅使用名称 AB足以区分类型)。好吧,如果您将其中一种类型与一些额外的属性(例如 )相交type AA = A & {randomPropName: any},现在您可以AA区分B。这种品牌被多次提及,甚至,甚至在 TypeScript 编译器代码本身中 使用。

所以人们是靠某处string & {hoobydooby: true}来区分的string & {scoobydooby: false}。如果这两者都减少到never,一切都会崩溃。所以他们不这样做。


他提到的另一个问题是:

我们允许此类类型存在主要是为了更容易发现它们的起源(例如,包含两个同名属性的对象类型的交集)。

因此,如果您有某种类型,例如{foo: string} & {foo: number},这可以减少到{foo: never}或什至只是never(毕竟,不{foo: never}应该存在任何类型的值),但我想错误消息变得更难以理解:

interface A {foo: string}
interface B {foo: number}
type C = A & B;
const c: C = {foo: "hello"}; // error! string is not assignable to string & number;
Run Code Online (Sandbox Code Playgroud)

这让您知道某个东西foo既是字符串又是数字,这是不可能的,但会提示您研究该C类型。否则:

const c: C = {foo: "hello"}; // error! string is not assignable to never
Run Code Online (Sandbox Code Playgroud)

我想这不太容易理解。

就我个人而言,我认为这是一个比第一个原因更弱的原因,但它是您问题的“最终”答案的一部分。


还有其他原因导致编译器不执行开发人员希望它执行的操作;最普遍的原因是时间。即使您可以证明假设的编译器操作不会破坏任何人的代码并有助于您的用例,您也需要证明它不会严重损害编译器的性能。在这种情况下,编译器应该如何积极地检查交集是否可能减少到neverA & B一般来说,如果不太可能减少到never,那么您为此所做的大部分检查都将是浪费精力。所以检查最好很快。

事实证明,这个性能问题是功能提议和建议被拒绝或最终没有纳入该语言的一个非常常见的原因。我没有看到它在这个特定问题的任何讨论中被具体列出,但如果它不是一个大因素,我会感到非常惊讶。


好的,希望有帮助。祝你好运!