
1.3.2 使用交叉类型组合类型
有时将多个类型合并为一个类型进行处理,对我们来说是很重要的能力。交叉类型由多个类型组合而成,具有各个类型的所有属性。通过下面这个简单的例子,我们可以了解交叉类型是什么样子。首先,我们将创建一个Grid类,以及一个应用到该Grid的Margin类,如下所示:

我们将创建一个交叉,其具有Grid的Width和Height属性,以及Margin的Left和Top属性。为此创建一个函数,将Grid和Margin作为参数,并返回包含上述属性的一个类型,如下所示:

本章后面介绍对象展开时还将回顾这个函数,说明如何避免重复使用属性的样板代码。
这段代码的关键在于consolidatedGrid的定义。我们使用&将类型连接起来创建交叉。因为我们想把Grid和Margin组合起来,所以使用<Grid & Margin>告诉编译器组合后的类型是什么样的。可以看到,我们不需要显式命名这个类型;编译器足够智能,能够替我们完成这项工作。
如果两个类型中有相同的属性,会发生什么?TypeScript会阻止我们把类型组合到一起吗?只要属性的类型相同,TypeScript就不反对我们使用相同的属性名称。为了说明这一点,我们将扩展Margin类,使其也包含Width和Height属性,如下所示:

如何处理这些属性取决于我们想要使用它们来做什么。在本例中,我们将把Margin的Width和Height加到Grid的Width和Height。因此,函数如下所示:

然而,如果我们试图重用相同的属性,但这些属性的类型不同,并且类型具有一些限制,就会发生问题。为了说明效果,我们将扩展Grid和Margin类来包含Weight。Grid类中的Weight属性是一个数字,而Margin类中的Weight是一个字符串,如下所示:

我们试着在ConsolidatedGrid函数中将Weight类型合并起来:

此时,TypeScript将给出下面的错误,说明这行代码有错:

虽然有一些方法来解决这个问题,例如为Grid中的Weight使用联合类型,然后解析输入,但是一般来说没有必要那么麻烦。如果类型不同,一般说明属性的行为是不同的,所以我们真正要做的是为其指定一个不同的名称。
虽然这里的示例介绍的是类,但是需要指出的是交叉并非只适用于类,也适用于接口、泛型和基本类型。
使用交叉时,还需要考虑其他一些规则。当属性名相同,但只有一个属性可选时,最终的属性将是必需的。我们将为Grid类和Margin类引入一个填充属性,并使得Margin中的Padding属性可选,如下所示:

因为我们提供了一个必需的Padding变量,所以不能修改交叉,如下所示:

由于不能保证边距填充会被赋值,所以编译器将尽力阻止此类操作。为了解决这个问题,我们将修改代码,使得如果margin的填充被赋值,就使用margin的填充,否则就使用grid的填充。为此,我们将做一个简单的修改:

这种看起来有些奇怪的语法称为三元运算符,是编写下面逻辑的一种简洁方法:如果margin.Padding有值,则使consolidatedGrid.Padding等于该值,否则使其等于grid.Padding。使用if/else语句也可以写出这种逻辑,但是因为在TypeScript和JavaScript中,这是一种常见的范式,所以有必要熟悉一下。