一、引言
昨天的文章七天学TypeScript(一) 是TypeScript的常用类型和基本使用。浏览一遍TypeScript的手册就会发现,TypeScript学习最重要的有三点:类型,类型,类型! 除了类型使用,当然还有类型安全。另外,本系列文章不求面面俱到,每篇说清楚一件事就很不错了,所以今天主要说类型收窄。对于类型收窄,我的理解是TypeScript根据编程语句上下文对变量或表达式的类型进行推断,从而和类型检查、bug检查相结合,尽可能明确语义,减少bug。
1、先学习字面量类型。 const声明的或用联合赋值的类型是字面量类型,不同于var或者let声明的具体类型。
-
例子:
function printText(s: string, alignment: "left" | "right" | "center") { // ... } printText("Hello, world", "left"); //报错,传递的centre是字符串,不是字面量(center才行) printText("G'day, mate", "centre");
2、枚举类型,一般高级语言都有,这里就直接举例了。
-
例子:
enum UserResponse { No = 0, Yes = 1, } function respond(recipient: string, message: UserResponse): void { // ... } respond("Princess Caroline", UserResponse.Yes); //再如: enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", }
二、类型收窄的N个方式
1、用typeof进行类型守护 。
-
例子:
function padLeft(padding: number | string, input: string) { if (typeof padding === "number") { return new Array(padding + 1).join(" ") + input; } return padding + input; }
2、布尔判断类型收窄,通常是if/else的条件判断
-
例子:
function printAll(strs: string | string[] | null) { if (strs && typeof strs === "object") { for (const s of strs) { console.log(s); } } else if (typeof strs === "string") { console.log(strs); } }
3、等值判断类型收窄,常用的等值判断大家都知道,有:===,!==,==, and!=
-
例子:
function printAll(strs: string | string[] | null) { if (strs !== null) { if (typeof strs === "object") { //参数strs是 string[] for (const s of strs) { console.log(s); } } else if (typeof strs === "string") { //参数strs是 string console.log(strs); } } }
4、in 操作符收窄
-
例子:
type Fish = { swim: () => void }; type Bird = { fly: () => void }; function move(animal: Fish | Bird) { if ("swim" in animal) { return animal.swim(); } return animal.fly(); }
5、instanceof 收窄。
-
例子:
function logValue(x: Date | string) { if (x instanceof Date) { // x: Date console.log(x.toUTCString()); } else { // x: string console.log(x.toUpperCase()); } }
6、赋值收窄。
-
例子:
let x = Math.random() < 0.5 ? 10 : "hello world!"; let x: string | number //因为是string和number的联合类型,所以此处赋值为number没问题 x = 1; console.log(x); //报错,赋值为布尔型 x = true; console.log(x);
7、复杂类型可以通过类型谓词
-
例子
//pet is Fish 就是类型谓词断定 function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; }
8、一个复杂类型判断的最佳实践,是通过共同属性判断类型。
-
例子
interface Circle { kind: "circle"; radius: number; } interface Square { kind: "square"; sideLength: number; } type Shape = Circle | Square;
9、switch中注意never类型
-
例子
//(1)来一个计算面积的函数,开始支持Circle和Square type Shape = Circle | Square; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.sideLength ** 2; default: const _exhaustiveCheck: never = shape; return _exhaustiveCheck; } } //(2)以后业务需要,增加了Triangle接口后会原方法会报错 interface Triangle { kind: "triangle"; sideLength: number; } type Shape = Circle | Square | Triangle; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.sideLength ** 2; default: const _exhaustiveCheck: never = shape; //报错:Triangle 不能赋值给never return _exhaustiveCheck; } }
注意:never可以赋值给别的类型;别人不能赋值给它,除非never赋值给never。
明天继续学习函数和对象类型,待续。
如果有错误或表述不清之处也请大家多批评指正。参考网址:www.typescriptlang.org