转发 TypeScript基础入门之高级类型的可辨识联合(Discriminated Unions)编程
你能够合并单例类型,联合类型,类型保护和类型别名来建立一个叫作 可辨识联合的高级模式,它也称作 标签联合或 代数数据类型。 可辨识联合在函数式编程颇有用处。 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具备3个要素:函数式编程
1. 具备普通的单例类型属性— 可辨识的特征。
2. 一个类型别名包含了那些类型的联合— 联合。
3. 此属性上的类型保护。函数
interface Interface1 { kind: 'interface1', property1: number, } interface Interface2 { kind: 'interface2', property2: number, property3: number, } interface Interface3 { kind: 'interface3', property4: number, property5: number, }
首先咱们声明了将要联合的接口。 每一个接口都有 kind属性但有不一样的字符串字面量类型。 kind属性称作 可辨识的特征或 标签。 其它的属性则特定于各个接口。 注意,目前各个接口间是没有联系的。 下面咱们把它们联合到一块儿:spa
type Type = Interface1 | Interface2 | Interface3;
如今咱们使用可辨识联合:code
function getType(i: Type) { switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
当没有涵盖全部可辨识联合的变化时,咱们想让编译器能够通知咱们。 好比,若是咱们添加了Interface4到Type,咱们同时还须要更新 area:blog
interface Interface4 { kind: 'interface4', property6: number, } type Type = Interface1 | Interface2 | Interface3 | Interface4; function getType(i: Type) { switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
有两种方式能够实现。 首先是启用 --strictNullChecks而且指定一个返回值类型:接口
function getType(i: Type): number { // error: returns number | undefined switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
由于 switch没有包涵全部状况,因此TypeScript认为这个函数有时候会返回 undefined。 若是你明确地指定了返回值类型为 number,那么你会看到一个错误,由于实际上返回值的类型为 number | undefined。 然而,这种方法存在些微妙之处且 --strictNullChecks对旧代码支持很差。第二种方法使用 never类型,编译器用它来进行完整性检查:ip
function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function getType(i: Type): number { // error: returns number | undefined switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; default: return assertNever(i); // error here if there are missing cases } }
这里, assertNever检查 s是否为 never类型—即为除去全部可能状况后剩下的类型。 若是你忘记了某个case,那么 s将具备一个真实的类型而且你会获得一个错误。 这种方式须要你定义一个额外的函数,可是在你忘记某个case的时候也更加明显。**Tips**
上面的代码是根据官网的逻辑写的,奇怪的是最后一步竟然在编译的时候报错了。报错信息以下字符串
$ tsc src/advanced_types_4.ts src/advanced_types_4.ts:38:26 - error TS2345: Argument of type 'Interface4' is not assignable to parameter of type 'never'. 38 return assertNever(i); // error here if there are missing cases ~