TypeScript类型缩小

类型缩小的概念

前面我们写了一些这样的代码:

function padLeft(padding: number | string, input: string): string {
  if (typeof padding === 'number') {
    return ' '.repeat(padding) + input
  }
  return padding + input
}

没有if判断时,无法执行语句return ’ '.repeat(padding) + input,使用if判读之后就可以执行了,这就是一个TypeScript类型缩小。
JavaScript 的运行时控制流结构(如 if/else、条件三元组、循环、真值检查等)都会影响TypeScript对这些类型的判断。换句话说,TypeScript会对代码语句进行检查,遵循我们的程序采用可能执行路径得出的更具体的可能类型。

typeof 类型保护

typeof一般返回的类型有:string、number、boolean、object、undefined、function、bigInt、symbol。TypeScript可以理解这些类型的缩小。
注意:在JavaScript中,null也算object类型。
在下面的例子中,我们企图先判断strs是否为对象类型,如果是的话就直接遍历,但是忘了string[]和null在JavaScript中都算object类型,故进入第一个判断的参数可能是string[]也可能是null类型,而null类型无法进行遍历,故会报错。

function printAll(strs: string | string[] | null) {
  if (typeof strs === 'object') {
    for (const s of strs) {//error strs可能是null类型
      console.log(s)
    }
  } else if (typeof strs === 'string') {
    console.log(strs)
  } else {
  }
}

真值缩小

if 这样的构造首先可以将条件、&&、||、if 语句、布尔否定 (!) 等 “强制转换” 到 boolean 来理解它们,然后根据结果是 true 还是 false 来选择它们的分支。
例如:0、NaN、0n(bigInt版本的0)、null、“ ”、undefined都会被强制转换为false。其他值都会被转换为true。
以上是推断为类型缩小的字面布尔类型true、false。你也可以直接使用Boolean函数来将值强制为Boolean类型。

Boolean("hello"); // type: boolean, value: true
!!"world"; // type: true,    value: true  !!会将一个值转换为其对应的字面布尔类型。

在if中使用条件来进行真值缩小非常常见。例如上面那个例子,我们只要在if语句中判断strs是否为null并上类型为object,就能将参数判断为数组。

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)
  } else {
  }

也可以先判断strs是否为null,再判断它是string类型还是object类型(string[])。

function printAll(strs: string | string[] | null) {
  if (strs) {
    if (typeof strs === "object") {
      for (const s of strs) {
        console.log(s);
      }
    } else if (typeof strs === "string") {
      console.log(strs);
    }
  }
}

带有否定分支的话就从否定分支中过滤掉。

function multiplyAll(values: number[] | undefined, factor: number): number[] | undefined {
  if (!values) {
    return values
  } else {
    return values.map((x) => x * factor)
  }
}

相等性缩小 使用switch !== === != ==

我们上面所举的printAll的例子没有处理str等于null的情况。相等性缩小可以处理。

function printAll(str: string | string[] | null) {
  if (str !== null) {
    if (str === 'string') {
      console.log(str)
    } else if (str === 'object') {
      for (const s of str) {
        console.log(s)
      }
    }
  }
}

JavaScript 对 == 和 != 的更宽松的相等性检查也正确地缩小了类型。
检查某物 == null 是否实际上不仅检查它是否是值 null,它还检查它是否可能是 undefined。这同样适用于 == undefined。
注意下面示例使用的是!=号,可以同时将null和undefined的情况过掉。

interface Container {
  value: number | null | undefined
}
function test(container: Container) {
  if (container.value != null) {
    console.log(container.value)
  } else {
    console.log('container.value可能是null或undefined')
  }
}

in运算符缩小

使用代码:“value” in x。其中 “value” 是字符串字面,x 是联合类型。判断联合类型中是否含属性名为value的类型。

type Fish = { swim: () => void }
type Bird = { fly: () => void }
function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    return animal.swim()
  }
  return animal.fly()
}

其中属性名必须带引号,函数可以是箭头函数也可以是普通函数。
可选属性存在于两侧进行缩小。这样的类型判断和我们写c语言的逻辑是一样的,符合要求的类型就会进入if判断中。

type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
function move(animal: Fish | Bird | Human) {
  if ("swim" in animal) {
    animal;  (parameter) animal: Fish | Human
  } else {
    animal;  (parameter) animal: Bird | Human
  }
}

instanceof缩小

在 JavaScript 中,x instanceof Foo 检查 x 的原型链是否包含 Foo.prototype。

function logValue(x: Date | string) {
  if (x instanceof Date) {//判断x中是否含有Date原型对象
    x.toLocaleDateString()//Date的方法
  } else {
    x.toLocaleUpperCase()//string的方法
  }
}

赋值

变量被赋值之后类型可能会缩小。
给变量x定义string|number类型。
在这里插入图片描述
对变量重新赋值一个1,此时中间的x还是string|number类型,但是再打印x时,他就变成了number类型。
在这里插入图片描述
再次给x赋值一个字符串,第三个x仍然是string|number类型,但是第四个打印的x被缩小为string类型。
在这里插入图片描述
变量的声明类型为string|number,中途我们可以随意地将string或者number类型赋值给x,因为TypeScript始终以声明类型来判断变量的可赋值性。

控制流分析

当分析一个变量时,控制流可以一次又一次地分裂和重新合并,并且可以观察到该变量在每个点具有不同的类型。

function example() {
  let x: string | number | boolean
  x = Math.random() < 0.5 //let x: string | number | boolean
  console.log(x) //let x: boolean
  if (Math.random() < 0.5) {
    x = 'hello' //let x: string | number | boolean
    console.log(x) //let x: string
  } else {
    x = 100 //let x: string | number | boolean
    console.log(x) //let x: number
  }
  return x //let x: string | number
}

类型谓词

interface Fish {
  swim: () => void
}
interface Bird {
  fly: () => void
}
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined//这里使用断言是为了避免ts警告,由于不知道pet到底是何种类型,这里还是做了!==判断
}
function getPet(): Fish | Bird {
  if (Math.random() < 0.5) {
    return { swim: () => {} }
  }
  return { fly: () => {} }
}
let pet = getPet()
if (isFish(pet)) {
  pet.swim() //let pet: Fish
} else {
  pet.fly() //let pet: Bird
}

类型谓词采用paramsName is Type的形式定义,其中paramsName一定是参数名称。

判别联合

先抛出一个问题:如果形状是圆形,就计算圆形的面积。如果形状是正方形,就计算正方形的面积。
我们想到第一种类型定义方法:

interface Shape {
  kind: 'circle' | 'square'
  radius?: number
  sideLen?: number
}
function handleShape(shape: Shape) {
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius ** 2 //“shape.radius”可能为“未定义”
  } else {
    return shape.sideLen ** 2 //“shape.sideLen”可能为“未定义”
  }
}

这个时候ts会报错,报错也是正常的,因为radius和sideLen都是可选值,你在传一个shape时,只有kind必传,有可能两个可选值都没传递,有可能kind是circle时你传递了sideLen,有可能kind是square时你传递了radius,这都会导致你无法计算面积,而ts也能推断这些。
我们可以把圆形和方形分开定义,但是给他们设置一个公共属性,让ts能够识别它属于什么形状。

interface Circle {
  kind: 'circle'
  radius: number
}
interface Square {
  kind: 'square'
  sideLen: number
}
type Shape = Circle | Square
function handleShape1(shape: Shape) {
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius ** 2//(parameter) shape: Circle
  } else {
    return shape.sideLen ** 2//(parameter) shape: Square
  }
}
//也可以使用switch语句
function handleShape2(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.sideLen ** 2
  }
}

never类型

never 类型表示永远不会出现的值的类型。通常情况下,never 类型用于表示抛出异常或者永远不会结束的函数的返回类型。例如,一个函数如果抛出异常,它的返回类型就可以定义为 never

穷举检查

never 类型可分配给每个类型;但是,没有类型可分配给 never(never 本身除外)
例如:switch语句中,当所有可能情况处理判断后,shape到了default分支,就可以将shape赋值给never类型的变量。

interface Circle {
  kind: 'circle'
  radius: number
}
interface Square {
  kind: 'square'
  sideLen: number
}
type Shape = Circle | Square
function handleShape(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.sideLen ** 2
    default:
      const _exhaustiveCheck: never = shape
      return _exhaustiveCheck
  }
}

但是,ts不允许可能出现的情况,未经判断,就赋值给never类型的变量,下面的代码会报错。

interface Circle {
  kind: 'circle'
  radius: number
}
interface Square {
  kind: 'square'
  sideLen: number
}
interface Triangle {
  kind: 'triangle'
  sideLen: number
}
type Shape = Circle | Square | Triangle
function handleShape(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.sideLen ** 2
    default:
      const _exhaustiveCheck: never = shape//error:不能将类型“Triangle”分配给类型“never”
      return _exhaustiveCheck
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/485893.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

星云小窝项目1.0——项目介绍(一)

星云小窝项目1.0——项目介绍&#xff08;一&#xff09; 文章目录 前言1. 介绍页面2. 首页2.1. 游客模式2.2. 注册用户后 3. 星云笔记3.1. 星云笔记首页3.2. 星云笔记 个人中心3.2. 星云笔记 系统管理3.3. 星云笔记 文章展示3.3. 星云笔记 新建文章 4. 数据中心5. 交流评论6. …

Qt读取本地系统时间的几种方式

一&#xff0c;使用Windows API函数GetLocalTime&#xff08;精确到毫秒&#xff09; typedef struct _SYSTEMTIME //SYSTEMTIME结构体定义 {   WORD wYear;//年   WORD wMonth;//月   WORD wDayOfWeek;//星期&#xff0c;0为星期日&#xff0c;1为星期一&#xff0c…

深入理解Java中的Reader类

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

【JAVA】通过JAVA实现用户界面的登录

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-wyCvaz0EBNwHcwsi {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

宋仕强说金航标kinghelm萨科微slkor都是网红品牌

宋仕强说金航标kinghelm萨科微slkor都是网红品牌&#xff0c;和宋仕强先生研究的“华强北”大ip一起&#xff0c;相互支持相互驱动&#xff0c;与金航标网站&#xff08;www.kinghelm.com.cn&#xff09;、萨科微网站&#xff08;www.slkormicro.com&#xff09;组合成为宣传矩…

Excel 导入指定分隔符的 csv 文件

原文&#xff1a;https://blog.iyatt.com/?p14373 基于 Excel 2024 预览版测试 csv 文件本身是纯文本的&#xff0c;同行数据之间通过一定的分隔符打断识别为不同的列&#xff0c;常用的分隔符是英文逗号&#xff0c;使用逗号分隔符的 csv 文件直接用 Excel 打开能正常识别单…

输入网址到网页显示全过程

TCP/IP ⽹络模型 对于同⼀台设备上的进程间通信&#xff0c;有很多种⽅式&#xff0c;⽐如有管道、消息队列、共享内存、信号等⽅式&#xff0c;⽽对于不同设备上的进程间通信&#xff0c;就需要⽹络通信&#xff0c;⽽设备是多样性的&#xff0c;所以要兼容多种多样的设备&am…

封装函数实现一维数组输入、输出以及冒泡排序

1. 代码 #include <stdio.h>int InputArray(int *pa, int len) {int i 0;for (i 0; i < len; i){scanf("%d", &pa[i]);}return 0; }int OutputArray(int *pa, int len) {int i 0;for (i 0; i < len; i){printf("%-2d ", pa[i]);}putc…

中标,我们是认真的!菊风再携手吉林银行打造智能双录系统

当前&#xff0c;数字化发展步伐持续加快&#xff0c;各个金融机构纷纷按下数字化转型的加速键&#xff0c;陆续推进数字化发展战略&#xff0c;加快数字金融建设。近期&#xff0c;吉林银行面对业务快速发展的需要&#xff0c;服务效率、人力成本等挑战日益凸显&#xff0c;逐…

YOLOv5全网独家改进: 注意力机制改进 | 并行化注意力设计(PPA)模块,红外小目标暴力涨点 | 2024年3月最新成果

💡💡💡本文独家改进:红外小目标涨点利器,在多个数据集下进行验证,其中并行化 patch-aware 注意力(PPA)模块,解决目标的大小微小以及红外图像中通常具有复杂的背景的问题点 💡💡💡红外小目标实现暴力涨点,只有几个像素的小目标识别率大幅度提升 改进结构图如…

MySQL的基本操作与增删改查管理操作

一、MySQL数据库sql语句 1.1 sql 命令 database数据库table表row行column列user用户select从数据表中获取数据updata更新数据库中的数据delete从数据库中删除数据insert into 向数据表插入数据create database创建新数据库alter database修改数据库create table创建新表alter…

第八节:深入讲解SMB中的Http组件

一、概述 Http组作是SMB中的核心组件之一&#xff0c;在第七节中讲解了如何简洁的进行web程序部署和运行&#xff0c;这只是它的功能之一。在本节中&#xff0c;我们将介绍Http组件的重要属性。 二、请求头Request 1、支持方法 支持POST、GET、PUT、DELETE、OPTIONS等方法&a…

AI数字人“搅局”直播电商

现如今&#xff0c;直播带货已然成为了备受消费者欢迎的一种新的购物模式&#xff0c;人们已经愈发习惯在直播间购物了。在直播带货热度居高不下背后&#xff0c;除了低价优势之外&#xff0c;还在于直播带货所具备的实时互动、全方位展示能够为消费者带去更加真实、直观、沉浸…

Java集合框架初学者指南:List、Set与Map的实战训练

Java集合框架是Java语言的核心部分&#xff0c;它提供了丰富的类和接口&#xff0c;用来高效地管理和操作大量数据。这个强大的工具箱包括多种集合类型&#xff0c;其中最为常用的是List、Set和Map。 1.List - 有序且可重复的数据清单 概念&#xff1a; List就像一个购物清单&…

python usb与下位机 硬件通信

需求分析 上周接到一个需求 用usb和硬件连接 轮询读取usb中指定功能码的指定个数的数据并生成一个桌面程序 刚接到这个需求时 我第一时间想到的就是使用python去尝试 期间也踩了很多的坑 第一版效果如下 特此记录 环境搭建 首先第一点就是将所需要的库进行安装 这里是我这…

Springboot2 restTemplate 使用UriComponentsBuilder时编码问题

文章目录 简要说明maven依赖样例代码 简要说明 在使用springboot2的restTemplate配合UriComponentsBuilder&#xff0c;UriComponentsBuilder拿到uri字符串时有编码过程&#xff0c;而restTemplate在execute时&#xff0c;底层也是有encode编码&#xff0c;这样就到时了双重编…

基于ssm的校园驿站管理系统论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对校园快递信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差…

详解机器学习概念、算法

目录 前言 一、常见的机器学习算法 二、监督学习和非监督学习 三、常见的机器学习概念解释 四、深度学习与机器学习的区别 基于Python 和 TensorFlow 深度学习框架实现简单的多层感知机&#xff08;MLP&#xff09;神经网络的示例代码&#xff1a; 欢迎三连哦&#xff01; 前言…

LeetCode 1027——最长等差数列

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 假设我们以 f[d][nums[i]]表示以 nums[i] 为结尾元素间距为 d 的等差数列的最大长度&#xff0c;那么&#xff0c;如果 nums[i]-d 也存在于 nums 数组中&#xff0c;则有&#xff1a; f [ d ] [ n u m s [ i ] ] …

我们是如何测试人工智能的(八)包含大模型的企业级智能客服系统拆解与测试方法 -- 大模型 RAG

大模型的缺陷 -- 幻觉 接触过 GPT 这样的大模型产品的同学应该都知道大模型的强大之处&#xff0c; 很多人都应该调戏过 GPT&#xff0c;跟 GPT 聊很多的天。 作为一个面向大众的对话机器人&#xff0c;GPT 明显是鹤立鸡群&#xff0c;在世界范围内还没有看到有能跟 GPT 扳手腕…