0
点赞
收藏
分享

微信扫一扫

一文读懂 typeScript 的类型收窄

耳一文 2022-02-19 阅读 81

在 typeScript 中,一个变量不会被限制为单一类型,如果你希望一个变量的值可以有多种类型,那么就可以使用 typeScript 提供的联合类型。

let str: string | string[]

当我们使用联合类型时,必须把当前值具体到实际类型,进行对应的操作。所以类型缩小就是从宽类型转化为窄类型的过程,常常用于处理联合类型变量的常量。

什么是类型缩小呢?

function getData( id: string|number ){
if( typeof id === "string" ){
cosole.log(id.toUpperCase())
}else{
cosole.log(id)
}
}

类型缩小:Type Narrowing。在实际应用中,我们经常会看到 typeof padding === "string" 的判断语句,根据不同的数据类型进行对应的处理,我们将这些特殊的检查称为类型防护,常见的类型防护有:

typeof 、平等缩小、instanceof、in 等等。

将类型细化为比声明更具体的类型的过程,称之为类型缩小。类型缩小的方法有很多,具体的有如下几种。

1、typeof 类型保护

在 TypeScript 中,typeof 类型守卫返回的是一个指定类型的字符串。

typeof 返回的值有:

object、string、number、boolean、undefined、function、bigint、symbol

function printAll(str: string | string[]) {
if (typeof str === "object") {
for(let s of str){
console.log(s);
}
}else if(typeof str === "string"){
console.log(str);
}
}
printAll("")
printAll(['a','b'])
printAll("12345")

2、真值缩小

在 typeScript 中可以使用:条件、&&、||、if语句、布尔否定(!)等,来组成布尔表达式。

function getUsersOnlineMessage(num: number) {
if (num) {
return `现在共有 ${num} 人在线!`
}
return "现在没人在线!"
}
getUsersOnlineMessage(NaN)
getUsersOnlineMessage(0)
getUsersOnlineMessage(null)
getUsersOnlineMessage(undefined)

上述方法四次调用,返回的都是“现在没人在线!”

NaN、0 、null 、undefined 、'' 、0n 这些类型作为 number 数据类型时,返回的都是 0 。所以执行结果都一样。

3、等值缩小

在 typeScript 中,经常使用 ===、!== 、== 、!= 运算符来进行等值检查,实现类型的缩小。

function example(x: string | number, y: string | boolean) {
if (x === y) {
x.toLocaleLowerCase()
y.toLocaleUpperCase()
} else {
//其他类型
}
}

4、in 操作符缩小

javaScript 中的 in 操作符,用于确定对象是否具有某名称的属性,如果指定的属性在对象或其原型链中,则返回 true 。

在 typeScript 中 in 操作符使用类似,使用语法为:

'name' in X

返回 true,表示 X 具有可选或必需属性或方法 name 。

返回false,表示需要具有可选或缺失属性的类型的值。

type Fish = {
swim: () => void
}
type Bird = {
fly: () => void
}
type Human = {
swim?: () => void
fly?: () => void
}
type Animal = Fish | Bird
function move(animal:Animal|Human) {
if("swim" in animal){
return (animal as Fish).swim()
}
return (animal as Bird).fly()
}

5、instanceof

instanceof 操作符,用来检查一个值是否是另一个值得实例。使用语法:

x instanceof Foo

检查 x 的原型链是否含有 Foo.prototype。

function show(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
} else {
console.log(x.toUpperCase());
}
}
show("abc") //ABC
show(new Date()) // Wed, 02 Feb 2022 08:55:09 GMT

6、类型谓词

为了定义一个用户定义的类型保护,只需要定义一个函数,让它的返回值是一个类型谓词就好了!使用语法:

parameteName is Type  //parameteName 必须是函数内的参数。
type Fish = {
swim: () => void
}
type Bird = {
fly: () => void
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined
}

上述实例中,pet is Fish 就是所谓的自定义类型谓词。函数内部返回 true 就表示 pet is Fish,反之 pet is Bird 。

7、受歧视的 union

interface Shape {
kind: 'circle' | 'square',
radius?: number,
sideLength?: number
}
function getArea(shape: Shape) {
return Math.PI * Math.pow(shape.radius, 2)
}
console.log(getArea({ kind: 'square', sideLength: 5 }));
console.log(getArea({ kind: 'circle', radius: 10 }));

上述实例,只能在 strict :false 模式下正常运行,运行结果分别为:NaN 和 314.1592653589793.。

在 strict:true 模式下运行,会报错。如图:

一文读懂 typeScript 的类型收窄_typescript

由于 radius 和 sideLength 是可选参数,不传 radius 时,默认的 radius 就是 undefined ,此时类型不一致就报错了,两种模式下编译结果不同,所以称联合类型受到歧视。

在严格模式下,可以添加断言 !,解决此问题,如:

return Math.PI * Math.pow( shape.radius!, 2 )

!表示假设 shape.radius 是一定存在的,此时在严格模式下也不会报错了,与非严格模式运行结果一致。

上述联合类型出现的问题,具体的解决办法有:

interface Circle {
kind: "circle",
radius: number
}
interface Square {
kind: "square",
sideLength: number
}
type Shape = Circle | Square
function getArea(shape: Shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * Math.pow(shape.radius, 2)
break;
case 'square':
return Math.pow(shape.sideLength, 2)
break
}
}
console.log(getArea({kind:"circle",radius:10}));
console.log(getArea({kind:"square",sideLength:10}));

也可以将 switch 改写成 if语句。

if (shape.kind === "circle") {
return Math.PI * Math.pow(shape.radius, 2)
} else {
return Math.pow(shape.sideLength, 2)
}

8、never 类型与穷尽性检查

never 表示不应该存在的状态,可以分配给每个类型,但是没有任何类型可以分配给 never ,除了 never 之外。具体而言,never 是永不返回函数的返回类型,也是变量在类型保护中永不为 true 的类型。

经常在 switch 中使用 never 进行详尽检查。修改上述实例,代码如下:

interface Circle {
kind: "circle",
radius: number
}
interface Square {
kind: "square",
sideLength: number
}
type Shape = Circle | Square
function getArea(shape: Shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * Math.pow(shape.radius, 2)
break;
case 'square':
return Math.pow(shape.sideLength, 2)
break
break:
const exhaustiveCheck: never = shape
return exhaustiveCheck
}
}

此时代码运行正常,如果给 shape 类型中添加一个形状,代码如下:

interface Triangle {
kind: "triangle",
sideLength: number
}
type Shape = Circle | Square | Triangle

此时代码就会报错,提示如图:

一文读懂 typeScript 的类型收窄_typescript_02

此时 never 就会帮助我们做穷尽性检查,检查出来没有添加 triangle 类型判断。

举报

相关推荐

0 条评论