1.3.1 借助联合类型使用不同的类型

首先介绍我最喜欢的特性之一:联合类型。当某个函数只有一个参数,但期望该参数是某个类型或另一个类型时,就可以使用联合类型。例如,假设有一个验证例程需要检查某个值是否落在特定区间内,并且该例程可从文本框接受一个string类型的值,或者从一个计算得到number类型的值。解决这个问题有多种方法,但是每种方法都有许多共通之处,所以我们首先创建一个简单的类,用来指定目标区间的最小值和最大值,并创建一个函数来执行验证,如下所示:

可能你之前没有见过类似的Constructor,它实际上相当于编写下面的代码:

如果需要检查参数或者以某种方式处理参数,则应该使用这种展开参数的形式。如果只是为私有字段赋值,那么第一种形式是一种非常优雅的方式,能够使代码更加整洁。

要确保只对string或number执行验证,有几种不同的解决方法。第一种方法是提供两个函数,让它们分别接受一种类型,如下所示:

这种方法能够解决我们的问题,但是不够优雅,并且没有利用TypeScript的强大功能。第二种方法是允许传入值,并不做限制,如下所示:

相比第一种实现,这种实现明显有了改进,因为现在函数只有一个签名,代码调用会更加一致。但是在这种实现中,我们仍然可以向方法传递一个无效的类型,如boolean。此时,代码能够成功编译,但在运行时将会失败。

如果想把验证逻辑限制为仅接受字符串或数字,则可以使用联合类型。新的实现与上个实现没有太大区别,但是能够提供我们想要的编译时类型安全,如下所示:

函数名称中的type | type签名表明使用了联合类型约束。它告诉编译器(和我们)这个方法的有效类型是什么。因为我们将输入类型限制为number或string,所以一旦能够确定类型不是number,就不需要检查typeof来判断类型是不是string,代码因而得到进一步简化。

在联合语句中,我们可以链接任意多的类型。虽然对类型的数量没有限制,但是我们必须确保对联合列表中的每个类型使用对应的typeof检查,这样才能正确处理类型。类型的顺序并不重要,number | string与string | number的效果是相同的。但是要记住,如果函数中组合了大量类型,则可能意味着该函数做的工作太多,此时应该检查代码,看是否可以把它分解为更小的函数。

联合类型还可以用来处理更多情况。TypeScript中有两种特殊类型:null和undefined。除非在编译代码时使用了-strictNullChecks选项,或者在tsconfig.json文件中设置了strictNullChecks = true,否则可以将这两种类型赋值给任何类型。本书建议设置这个值,这样代码只会在必要的地方处理null,从而避免由于函数接受null值而产生意外情况。如果想允许使用null或undefined,则只需把它们添加为联合类型。