🚀【TypeScript入门手册】记录了出场率较高的Ts概念,旨在帮助大家了解并熟悉Ts
🎉 本系列会持续更新并更正,重点关照大家感兴趣的点,欢迎同学留言交流,在进阶之路上,共勉!
star本项目给作者一点鼓励吧
一、认识Narrowing
试想我们有这样一个函数,函数名为 padLeft:
该函数实现的功能是:如果参数 padding 是一个数字,我们就在 input 前面添加同等数量的空格,而如果 padding 是一个字符串,我们就直接添加到 input 前面。
让我们实现一下这个逻辑:
functionpadLeft(padding:number|string, input:string){returnnewArray(padding +1).join(" ")+ input;// 运算符“+”不能应用于类型“string | number”和“number”}
如果这样写的话,编辑器里
padding + 1
这个地方就会标红,显示一个错误。这是 TypeScript 在警告我们,如果把一个
number
类型 (即例子里的数字 1 )和一个
number | string
类型相加,也许并不会达到我们想要的结果。换句话说,我们应该先检查下
padding
是否是一个
number
:
functionpadLeft(padding:number|string, input:string){if(typeof padding ==="number"){returnnewArray(padding +1).join(" ")+ input;}return padding + input;}console.log(padLeft(10,"哈哈"));
这个代码看上去也许没有什么有意思的地方,但实际上,TypeScript 在背后做了很多东西。
TypeScript 要学着分析这些使用了静态类型的值在运行时的具体类型。目前 TypeScript 已经实现了比如 if/else 、三元运算符、循环、真值检查等情况下的类型分析。
在我们的 if 语句中,TypeScript 会认为
typeof padding === number
是一种特殊形式的代码,我们称之为类型保护
(type guard)
,TypeScript 会沿着执行时可能的路径,分析值在给定的位置上最具体的类型。
TypeScript 的类型检查器会考虑到这些类型保护和赋值语句,而这个将类型推导为更精确类型的过程,我们称之为**收窄 (narrowing)**。 在编辑器中,我们可以观察到类型的改变:
functionpadLeft(padding:number|string, input:string){if(typeof padding ==="number"){returnnewArray(padding +1).join(" ")+ input;// (parameter) padding: number}return padding + input;// (parameter) padding: string}
从上图中可以看到在 if 语句中,和剩余的 return 语句中,padding 的类型都推导为更精确的类型。
接下来,我们就介绍 narrowing 所涉及的各种内容。
二、typeof收窄(type guards)
JavaScript 本身就提供了 typeof 操作符,可以返回运行时一个值的基本类型信息,会返回如下这些特定的字符串:
- “string”
- “number”
- “bigInt”
- “boolean”
- “symbol”
- “undefined”
- “object”
- “function”
typeof
操作符在很多 JavaScript 库中都有着广泛的应用,而 TypeScript 已经可以做到理解并在不同的分支中将类型收窄。在 TypeScript 中,检查
typeof
返回的值就是一种类型保护。
functiongetText(str:number|string):string{if(typeof str ==="number"){return`${str} isNumber`;// (parameter) str: number}else{return`${str} isString`;// (parameter) str: string}}
三、等值收窄(Equality narrowing)
Typescript 也会使用 switch 语句和等值检查比如 == !== == != 去收窄类型。比如:
functiongetText(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}}
在这个例子中,我们判断了
x
和
y
是否完全相等,如果完全相等,那他们的类型肯定也完全相等。而
string
类型就是
x
和
y
唯一可能的相同类型。所以在第一个分支里,
x
和
y
就一定是 string 类型。判断具体的字面量值也能让 TypeScript 正确的判断类型。
四、in 操作符收窄
JavaScript 中有一个 in 操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。举个例子,在 “value” in x 中,“value” 是一个字符串字面量,而 x 是一个联合类型:
typeDog={ ww:"1"; mm?:"2"};typeCat={ mm:"1"};functioninDemo(animal: Dog | Cat):void{if("ww"in animal){console.log(animal);// (parameter) animal: Dog}else{console.log(animal);// (parameter) animal: Cat}}
通过 “ww” in animal ,我们可以准确的进行类型收窄。
而如果有可选属性,Ts也会检测出来:
typeDog={ ww:"1"; mm?:"2"};typeCat={ mm:"1"};functioninDemo(animal: Dog | Cat):void{if("mm"in animal){console.log(animal);// (parameter) animal: Dog | Cat}else{console.log(animal);// (parameter) animal: Cat}}
五、instanceof 收窄
instanceof 也是一种类型保护,TypeScript 也可以通过识别 instanceof 正确的类型收窄:
functioninstanceofDemo(a: object |number):void{if(a instanceofString){console.log(a);// (parameter) a: String}else{console.log(a);// (parameter) a: number | object}}
六、赋值语句(Assignments)
TypeScript 可以根据赋值语句的右值,正确的收窄左值。
let x = Math.random()>0.5?"abc":123;
x =1;// let x: string | numberconsole.log(x);// let x: number
x ="1";// let x: string | numberconsole.log(x);// let x: string
注意这些赋值语句都有有效的,即便我们已经将 x 改为 number 类型,但我们依然可以将其更改为 string 类型,这是因为 x 最初的声明为 string | number,赋值的时候只会根据正式的声明进行核对。
写在最后
本篇文章是《Typescript基础入门》第二篇,收窄是一组比较难理解的思路,这里仅提到部分常见的形式,一起共勉吧!
参考:
- TypeScript4 中文文档
关于我:
- 花名:余光
- 邮箱:webbj97@163.com
- csdn:传送门
其他沉淀:
- Github: Js版LeetCode题解
- 余光的前端成长笔记
- 高频手撕代码系列
版权归原作者 余光、 所有, 如有侵权,请联系我们删除。