二、类型收窄(Narrowing)
1.认识类型收窄
function handleType(val: string | number){if(typeof val === 'string'){return val.length; // (parameter) val: string}return val; // (parameter) val: number}
这个代码看上去也许没有什么有意思的地方,但实际上,TypeScript 在背后做了很多东西。
TypeScript 要学着分析这些使用了静态类型的值在运行时的具体类型。目前 TypeScript 已经实现了比如 if/else 、三元运算符、循环、真值检查等情况下的类型分析。
在我们的 if 语句中,TypeScript 会认为typeof val === 'string'
是一种特殊形式的代码,我们称之为类型保护(type guard)
,TypeScript 会沿着执行时可能的路径,分析值在给定的位置上最具体的类型。
2.类型保护(type guards)
JavaScript 本身就提供了 typeof 操作符,可以返回运行时一个值的基本类型信息,会返回如下这些特定的字符串:
“string”“number”“bigInt”“boolean”“symbol”“undefined”“object”“function”
typeof
操作符在很多 JavaScript 库中都有着广泛的应用,而 TypeScript 已经可以做到理解并在不同的分支中将类型收窄。在 TypeScript 中,检查typeof
返回的值就是一种类型保护。
function getText(str: number | string): string {if (typeof str === "number") {return `${str} isNumber`; // (parameter) str: number} else {return `${str} isString`; // (parameter) str: string}}
3.等值收窄(Equality narrowing)
Typescript 也会使用 switch 语句和等值检查比如 =、!、==、!=,去收窄类型。比如:
function getText(a: string | boolean, b: string | null): void {if (a === b) {console.log(a);console.log(b);// (parameter) a: string// (parameter) b: string} else {console.log(a);console.log(b);// (parameter) a: string | boolean// (parameter) b: string | null}}
ts的分析可能是这样的:
判断a
和b
是否完全相等完全相等,string
类型是a
和b
唯一可能的相同类型。所以a
和b
一定是 string 类型。判断具体的字面量值也能让 TypeScript 正确的判断类型。不等,那就各自安好,不再纠结了~
4.四、in 操作符收窄
JavaScript 中有一个 in 操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。
type Dog = {ww: "1"; mm?: "2" };type Cat = {mm: "1" };function inDemo(animal: Dog | Cat): void {if ("ww" in animal) {console.log(animal); // (parameter) animal: Dog} else {console.log(animal); // (parameter) animal: Cat}}
通过 “ww” in animal中,我们可以推断出,animal一定是Dog类型,类型收窄。
而如果有可选属性,Ts也会检测出来:
type Dog = {ww: "1"; mm?: "2" };type Cat = {mm: "1" };function inDemo(animal: Dog | Cat): void {if ("mm" in animal) {console.log(animal); // (parameter) animal: Dog | Cat} else {console.log(animal); // (parameter) animal: Dog}}
mm 在 animal中是不能正确收窄的,但如果不在 animal 中,则可以推断出其是 Dog 类型。
5.instanceof 收窄
instanceof 也是一种类型保护,TypeScript 也可以通过识别 instanceof 正确的类型收窄:
function instanceofDemo(a: object | number): void {if (a instanceof String) {console.log(a);// (parameter) a: String} else {console.log(a);// (parameter) a: number | object}}
6.赋值语句(Assignments)
TypeScript 可以根据赋值语句的右值,正确的收窄左值。
let x = Math.random() > 0.5 ? "abc" : 123; // x: string | numberx = 1; // x: numberx = "1"; // let x: string
注意这些赋值语句都有有效的,即便我们已经将 x 改为 number 类型,但我们依然可以将其更改为 string 类型,这是因为 x 最初的声明为 string | number,赋值的时候只会根据正式的声明进行核对。
写在最后
本篇文章是《Typescript基础入门》第二篇,收窄是一组比较难理解的思路,这里仅提到部分常见的形式,一起共勉吧!
参考:
TypeScript4 中文文档
欢迎Star⭐️
Github: Js版LeetCode题解余光的前端成长笔记高频手撕代码系列