0


【Typescript入门手册】类型收窄(Narrowing)

🚀【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 中文文档

关于我:

其他沉淀:

  • Github: Js版LeetCode题解
  • 余光的前端成长笔记
  • 高频手撕代码系列


本文转载自: https://blog.csdn.net/jbj6568839z/article/details/122178864
版权归原作者 余光、 所有, 如有侵权,请联系我们删除。

“【Typescript入门手册】类型收窄(Narrowing)”的评论:

还没有评论