Typescript 不应该';t`string&;任何[]结果都是“从不”?

Typescript 不应该';t`string&;任何[]结果都是“从不”?,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 | n

我注意到打字稿有点奇怪。我有一个类型联合,它包含一些数组类型(
string[]
number[]
)和一些非数组类型(
string
number
)。如果我使用类型推断,一切都会按预期进行:

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

if (Array.isArray(foo))
{
    foo // string[] | number[]
}
else
{
    foo // string | number
}
但是,如果我想将类型直接限制为数组类型并使用类型交集,我会得到一些意想不到的结果:

declare foo: bar & any[];

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

foo // (string & any[]) | (number & any[]) | (string[] & any[]) | (number[] & any[])
为什么会这样?
不应该将
string和any[]
计算为
never
string[]&any[]
计算为
string[]


[]

我认为您在这里看到的是联合类型的分布式特性。e、 g.如果将一个联合体
a | B | C
与某物(比如
D
)相交,则该相交点将被分配为
a&D | B&D | C&D

type U = A | B | C;
type Z = U & D; // distributes as A & D | B & D | C & D
第二个细微差别是交叉口类型不会以您的思维方式“崩溃”(不确定这里的实际术语是什么)。一个简单的例子是
type X=Array&Array。类型X不会折叠为
数组
,而是保持原来声明的交叉点

值得注意的是,这似乎与交叉口类型是否导致<代码>从不场景无关。例如
type X={X:string}&{y:string}
也保持声明状态,而不是显示为
{x:string,y:string}

编译器用来限制交集的部分是当您实际尝试分配不满足交集类型的内容时

e、 g

type X=Array&Array;//此类型将保持声明的状态
常量x:x=['some string'];//这会引起抱怨
常数y:X=[4];//这会奏效的
如果要限制类型,可以使用条件类型而不是交集来过滤并集

type Bar = string | number | string[] | number[];

declare const foo: Exclude<Bar, Array<any>>; // string | number
type Bar=string | number | string[]| number[];
声明常量foo:Exclude;//字符串|编号

我认为您在这里看到的是联合类型的分布式特性。e、 g.如果将一个联合体
a | B | C
与某物(比如
D
)相交,则该相交点将被分配为
a&D | B&D | C&D

type U = A | B | C;
type Z = U & D; // distributes as A & D | B & D | C & D
第二个细微差别是交叉口类型不会以您的思维方式“崩溃”(不确定这里的实际术语是什么)。一个简单的例子是
type X=Array&Array。类型X不会折叠为
数组
,而是保持原来声明的交叉点

值得注意的是,这似乎与交叉口类型是否导致<代码>从不场景无关。例如
type X={X:string}&{y:string}
也保持声明状态,而不是显示为
{x:string,y:string}

编译器用来限制交集的部分是当您实际尝试分配不满足交集类型的内容时

e、 g

type X=Array&Array;//此类型将保持声明的状态
常量x:x=['some string'];//这会引起抱怨
常数y:X=[4];//这会奏效的
如果要限制类型,可以使用条件类型而不是交集来过滤并集

type Bar = string | number | string[] | number[];

declare const foo: Exclude<Bar, Array<any>>; // string | number
type Bar=string | number | string[]| number[];
声明常量foo:Exclude;//字符串|编号

考虑到两个非重叠集合的交集为空的直觉,可以合理地预期完全不相交类型的交集应计算为
从不
。这在不同时期都是如此

事实上,从TypeScript 2.6开始,这种减少到<代码>从未的情况就一直存在。具体地说,像
(“a”|“b”)和“c”
这样的类型变为
从不
,而
“a”和“c”
则不会。这样做的目的是,合并并集和交集不会导致大量的并集类型

但是,介绍这个实现的pull请求的描述让我们对您的问题有了一些了解:“为什么编译器不一直这样做?”?下面的引用来自TypeScript语言的主要维护者/架构师之一

他提到了一个问题:

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

这种“标记”或“品牌化”是在TypeScript中进行模拟的一种方式。TypeScript用于比较类型,这意味着编译器不会区分两种类型
A
B
,如果它们具有相同的形状。有时,您希望编译器能够以不同的方式处理两种完全相同的类型(这是Java等名义类型语言中的默认行为,其中只有名称
a
B
就足以区分这些类型)。好的,如果您将其中一个类型与一些额外属性相交,比如
typeaa=A&{randomPropName:any}
,那么现在您可以区分
AA
B
。这种品牌化非常多,甚至在TypeScript编译器代码本身中也是如此

所以在某些地方,人们依靠
string&{hoobydooby:true}
来区别
string&{scoobydooby:false}
。如果这两个选项都减少为“从不”
,则一切都会中断。所以他们不会这么做


他提到的另一个问题是:

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

因此,如果你有一些类型,比如
{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;
这给了你一些想法