内容简介:阅读须知:本文示例的运行环境是 TypeScript 官网的TypeScript 可辨识联合(Discriminated Unions)类型,也称为代数数据类型或标签联合类型。它包含 3 个要点:可辨识、联合类型和类型守卫。这种类型的本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。
阅读须知:本文示例的运行环境是 TypeScript 官网的 Playground ,对应的编译器版本是 v3.8.3 。
TypeScript 可辨识联合(Discriminated Unions)类型,也称为代数数据类型或标签联合类型。它包含 3 个要点:可辨识、联合类型和类型守卫。
这种类型的本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。
一、可辨识
可辨识要求联合类型中的每个元素都含有一个单例类型属性,比如:
enum CarTransmission { Automatic = 200, Manual = 300 } interface Motorcycle { vType: "motorcycle"; // discriminant make: number; // year } interface Car { vType: "car"; // discriminant transmission: CarTransmission } interface Truck { vType: "truck"; // discriminant capacity: number; // in tons }
在上述代码中,我们分别定义了 Motorcycle
、 Car
和 Truck
三个接口,在这些接口中都包含一个 vType
属性,该属性被称为可辨识的属性,而其它的属性只跟特性的接口相关。
二、联合类型
基于前面定义了三个接口,我们可以创建一个 Vehicle
联合类型:
type Vehicle = Motorcycle | Car | Truck;
现在我们就可以开始使用 Vehicle
联合类型,对于 Vehicle
类型的变量,它可以表示不同类型的车辆。
三、类型守卫
下面我们来定义一个 evaluatePrice
方法,该方法用于根据车辆的类型、容量和评估因子来计算价格,具体实现如下:
const EVALUATION_FACTOR = Math.PI; function evaluatePrice(vehicle: Vehicle) { return vehicle.capacity * EVALUATION_FACTOR; } const myTruck: Truck = { vType: "truck", capacity: 9.5 }; evaluatePrice(myTruck);
对于以上代码,TypeScript 编译器将会提示以下错误信息:
Property 'capacity' does not exist on type 'Vehicle'. Property 'capacity' does not exist on type 'Motorcycle'.
原因是在 Motorcycle 接口中,并不存在 capacity
属性,而对于 Car 接口来说,它也不存在 capacity
属性。那么,现在我们应该如何解决以上问题呢?这时,我们可以使用类型守卫。下面我们来重构一下前面定义的 evaluatePrice
方法,重构后的代码如下:
function evaluatePrice(vehicle: Vehicle) { switch(vehicle.vType) { case "car": return vehicle.transmission * EVALUATION_FACTOR; case "truck": return vehicle.capacity * EVALUATION_FACTOR; case "motorcycle": return vehicle.make * EVALUATION_FACTOR; } }
在以上代码中,我们使用 switch
和 case
运算符来实现类型守卫,从而确保在 evaluatePrice
方法中,我们可以安全地访问 vehicle
对象中的所包含的属性,来正确的计算该车辆类型所对应的价格。
四、穷举检查
假设我们想要往前面已经定义的 Vehicle
联合类型,添加新的类型,那么会出现什么问题呢?下面我们来实际验证一下。首先我们先新增一个新的 Bicycle
接口,接着继续更新 Vehicle
联合类型的定义,具体代码如下:
interface Bicycle { vType: "bicycle"; make: number; } type Vehicle = Motorcycle | Car | Truck | Bicycle;
更新完以上代码之后,TypeScript 编译器会提示以下的错误信息:
Not all code paths return a value.
为什么会提示这个错误信息呢?原因是因为我们之前创建的 evaluatePrice
方法还没处理 Bicycle
类型。那好,我们再来更新一下 evaluatePrice
方法,即增加对新增 Bicycle
类型的处理逻辑:
function evaluatePrice(vehicle: Vehicle) { switch(vehicle.vType) { case "car": return vehicle.transmission * EVALUATION_FACTOR; case "truck": return vehicle.capacity * EVALUATION_FACTOR; case "motorcycle": return vehicle.make * EVALUATION_FACTOR; case "bicycle": return vehicle.make * EVALUATION_FACTOR; } }
虽然现在问题已经解决了,但如果以后还要新增新的车辆类型呢?针对这个问题,有没有更好的解决方案呢?答案是有的,可以利用 TypeScript 中的 never 类型,具体代码如下:
function evaluatePrice(vehicle: Vehicle) { switch(vehicle.vType) { case "car": return vehicle.transmission * EVALUATION_FACTOR; case "truck": return vehicle.capacity * EVALUATION_FACTOR; case "motorcycle": return vehicle.make * EVALUATION_FACTOR; case "bicycle": return vehicle.make * EVALUATION_FACTOR; default: const invalidVehicle: never = vehicle; throw new Error(`Unknown vehicle: ${invalidVehicle}`); } }
在上面代码中,我们新增了默认的处理分支,在该分支中,我们把收窄为 never 类型的 vehicle 变量赋值给同为 never 类型的 invalidVehicle 变量,这样有什么好处呢?现在我们来把前面新增的 Bicycle
类型的处理逻辑注释掉,这时 TypeScript 编译器也会提示错误信息,但此时的错误信息是这样的:
Type 'Bicycle' is not assignable to type 'never'.
相比之前的错误信息,是不是更加直观了。在 evaluatePrice
方法中,我们新增了默认的处理分支,穷举了所有可能的车辆类型。 此外我们还引入了 never 类型避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。
五、参考资源
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 假 AI?如何辨识 AI 界的 snake oil
- Chrome 将支持 Android 和 Mac 上指纹辨识相关功能
- 基于 CNN 的台铁订票验证码辨识以及验证性高的训练集产生器
- golang的值类型,指针类型和引用类型&值传递&指针传递
- Scala 类型的类型(三)
- Scala 类型的类型(二)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
王牌创业者:风口游戏
澈言 / 百花洲文艺出版社 / 2018-2 / 48.00
《王牌创业者:风口游戏》是一部围绕互联网创业展开的商战小说:故事的主人公莫飞是“毕业即创业”的当代年轻创业者的典型代表,他大学在校时就凭借创业项目拿到了天使融资,创业几年后,当产品估值越做越大时,他却忽然遭遇创业伙伴及投资人的联手陷害,失去了自己一手建立的公司。 此时, 莫飞的女友林姿参加了一场声势浩大的创业比赛,并一举夺魁,直进决赛。可在决赛中,突如其来的一场新闻事件让她名誉扫地。最终,为......一起来看看 《王牌创业者:风口游戏》 这本书的介绍吧!