3、Typescript中补充的六个类型

news2024/9/22 6:52:28

1、元组

        元组可以看做是数组的拓展,它表示已知元素数量和类型的数组。确切地说,是已知数组中每一个位置上的元素的 类型,来看例子:

let tuple: [string, number, boolean];
tuple = ["a", 2, false];
tuple = [2, "a", false]; // error 不能将类型“number”分配给类型“string”。 不能将类型“string”分配给类型“number”。
tuple = ["a", 2]; // error Property '2' is missing in type '[string, number]' but required in type '[string, number, boolean]'

        可以看到,上面我们定义了一个元组 tuple,它包含三个元素,且每个元素的类型是固定的。当我们为 tuple 赋值 时:各个位置上的元素类型都要对应,元素个数也要一致。我们还可以给单个元素赋值:

tuple[1] = 3;

        这里我们给元组 tuple 的索引为 1 即第二个元素赋值为 3,第二个元素类型为 number,我们赋值给 3,所以没有问题。当我们访问元组中元素时,TypeScript 会对我们在元素上做的操作进行检查:

tuple[0].split(":"); // right 类型"string"拥有属性"split"
tuple[1].split(":"); // error 类型“number”上不存在属性“split”

        上面的例子中,我们访问的 tuple 的第二个元素的元素类型为 number,而数值没有 split 方法,所以会报错。在 2.6 版本之前,TypeScript 对于元组长度的校验和 2.6 之后的版本有所不同,我们来看下面的例子,前后版本对 于该情况的处理:

let tuple: [string, number];
tuple = ["a", 2]; // right 类型和个数都对应,没问题
// 2.6版本之前如下也不会报错
tuple = ["a", 2, "b"];
// 2.6版本之后如下会报错
tuple = ["a", 2, "b"]; // error 不能将类型“[string, number, string]”分配给类型“[string, number]”。 属性“length”的类型不兼容。

        这个赋给元组的值有三个元素,是比我们定义的元组类型元素个数多的:

  • 在 2.6 及之前版本中,超出规定个数的元素称作越界元素,但是只要越界元素的类型是定义的类型中的一种即 可。比如我们定义的类型有两种:string 和 number,越界的元素是 string 类型,属于联合类型 string | number , 所以没问题,联合类型的概念我们后面会讲到。
  • 在 2.6 之后的版本,去掉了这个越界元素是联合类型的子类型即可的条件,要求元组赋值必须类型和个数都对应。

        在 2.6 之后的版本,[string, number]元组类型的声明效果上可以看做等同于下面的声明:

interface Tuple extends Array<number | string> {
 0: string;
 1: number;
 length: 2;
}

        上面这个声明中,我们定义接口 Tuple ,它继承数组类型,并且数组元素的类型是 number 和 string 构成的联合类型,这样接口 Tuple 就拥有了数组类型所有的特性。并且我们明确指定索引为0的值为 string 类型,索引为1的值为 number 类型,同时我们指定 length 属性的类型字面量为 2,这样当我们再指定一个类型为这个接口 Tuple 的时 候,这个值必须是数组,而且如果元素个数超过2个时,它的length就不是2是大于2的数了,就不满足这个接口定义 了,所以就会报错;当然,如果元素个数不够2个也会报错,因为索引为0或1的值缺失。

        如果你想要和 2.6 及之前版本一样的元组特性,那你可以这样定义接口:

interface Tuple extends Array<number | string> {
 0: string;
 1: number;
}

        也就是去掉接口中定义的 length: 2 ,这样 Tuple 接口的 length就是从 Array继承过来的 number 类型,而不用必须是 2 了。

2、枚举

        enum 类型在 C++这些语言中比较常见,TypeScript 在 ES 原有类型基础上加入枚举类型,使我们在 TypeScript 中 也可以给一组数值赋予名字,这样对开发者来说较为友好。比如我们要定义一组角色,每一个角色用一个数字代 表,就可以使用枚举类型来定义:

enum Roles {
 SUPER_ADMIN,
 ADMIN,
 USER
}

        上面定义的枚举类型 Roles 里面有三个值,TypeScript 会为它们每个值分配编号,默认从 0 开始,依次排列,所以 它们对应的值是:

enum Roles {
 SUPER_ADMIN = 0,
 ADMIN = 1,
 USER = 2
}

        当我们使用的时候,就可以使用名字而不需要记数字和名称的对照关系了:

const superAdmin = Roles.SUPER_ADMIN;
console.log(superAdmin); // 0

        你也可以修改这个数值,比如你想让这个编码从 1 开始而不是 0,可以如下定义:

enum Roles {
 SUPER_ADMIN = 1,
 ADMIN,
 USER
}

        这样当你访问 Roles.ADMIN时,它的值就是 2 了。你也可以为每个值都赋予不同的、不按顺序排列的值:

enum Roles {
 SUPER_ADMIN = 1,
 ADMIN = 3,
 USER = 7
}

        通过名字 Roles.SUPER_ADMIN 可以获取到它对应的值 1,同时你也可以通过值获取到它的名字,以上面任意数值 这个例子为前提:

console.log(Roles[3]); // 'ADMIN'

3、Any

        JavaScript 的类型是灵活的,程序有时也是多变的。有时,我们在编写代码的时候,并不能清楚地知道一个值到底 是什么类型,这时就需要用到 any 类型,即任意类型。我们来看例子:

let value: any;
value = 123;
value = "abc";
value = false;

        你可以看到,我们定义变量 value,指定它的类型为 any,接下来赋予任何类型的值都是可以的。我们还可以在定义数组类型时使用 any 来指定数组中的元素类型为任意类型:

const array: any[] = [1, "a", true];

        但是请注意,不要滥用 any,如果任何值都指定为 any 类型,那么 TypeScript 将失去它的意义。所以如果类型是未知的,更安全的做法是使用unknown类型。

4、void

        void 和 any 相反,any 是表示任意类型,而 void 是表示没有任意类型,就是什么类型都不是,这在我们定义函 数,函数没有返回值时会用到:

const consoleText = (text: string): void => {
 console.log(text);
};

        void 类型的变量只能赋值为 undefined 和 null ,其他类型不能赋值给 void 类型的变量、

5、never

        never 类型指那些永不存在的值的类型,它是那些总会抛出异常或根本不会有返回值的函数表达式的返回值类型, 当变量被永不为真的类型保护(后面章节会详细介绍)所约束时,该变量也是 never 类型。这个类型比较难理解,我们先来看几个例子:

const errorFunc = (message: string): never => {
 throw new Error(message);
};

        这个 errorFunc 函数总是会抛出异常,所以它的返回值类型时 never,用来表明它的返回值是永不存在的。

const infiniteFunc = (): never => {
 while (true) {}
};

        infiniteFunc 也是根本不会有返回值的函数,它和之前讲 void 类型时的 consoleText 函数不同, consoleText 函数没有返回值,是我们在定义函数的时候没有给它返回值,而 infiniteFunc 是死循环是根本不会返回值的,所以它们二者还是有区别的。

        never 类型是任何类型的子类型,所以它可以赋值给任何类型;而没有类型是 never 的子类型,所以除了它自身没有任何类型可以赋值给 never 类型,any 类型也不能赋值给 never 类型。我们来看例子:

let neverVariable = (() => {
 while (true) {}
})();
neverVariable = 123; // error 不能将类型"number"分配给类型"never"

        上面例子我们定义了一个立即执行函数,也就是 "let neverVariable = " 右边的内容。右边的函数体内是一个死循环, 所以这个函数调用后的返回值类型为 never,所以赋值之后 neverVariable 的类型是 never 类型,当我们给 neverVariable 赋值 123 时,就会报错,因为除它自身外任何类型都不能赋值给 never 类型。

6、unknown

        unknown类型是TypeScript在3.0版本新增的类型,它表示未知的类型,这样看来它貌似和any很像,但是还是有区别的,也就是所谓的“unknown相对于any是安全的”。怎么理解呢?我们知道当一个值我们不能确定它的类型的时 候,可以指定它是any类型;但是当指定了any类型之后,这个值基本上是“废”了,你可以随意对它进行属性方法的访问,不管有的还是没有的,可以把它当做任意类型的值来使用,这往往会产生问题,如下:

let value: any
console.log(value.name)
console.log(value.toFixed())
console.log(value.length)

        上面这些语句都不会报错,因为value是any类型,所以后面三个操作都有合法的情况,当value是一个对象时,访问 name属性是没问题的;当value是数值类型的时候,调用它的toFixed方法没问题;当value是字符串或数组时获取它的length属性是没问题的。

        而当你指定值为unknown类型的时候,如果没有通过基于控制流的类型断言来缩小范围的话,是不能对它进行任何操作的,unknown类型的值不是可以随便操作的。

7、高级类型

7.1、交叉类型

        交叉类型就是取多个类型的并集,使用 & 符号定义,被&符链接的多个类型构成一个交叉类型,表示这个类型同时 具备这几个连接起来的类型的特点,来看例子:

//交叉类型
interface ClassA{
    name:string;
    age:number
}

interface ClassB{
    name:string;
    phone:number;
}

//将接口ClassA和接口ClassB通过&进行合并创建一个新的接口类型Class
type Class = ClassA & ClassB
let info:Class = {
    name:'zhagsan',
    age:18,
    phone:1573875555
}

console.log("交叉结果:",info)//交叉结果: {"name": "zhagsan","age": 18,"phone": 1573875555}

要点说明:

任何类型都能通过&合并成新的类型吗?

        这肯定是不行的,原子类型进行合并是没有任何意义,因为它们合并后的类型是never,比如string&number,这肯定是错误的,因为不可能有既满足字符串又满足数字类型。

合并的接口类型中具有同名属性,该怎么处理?

        这里分两种情况,如果同名属性的类型相同则合并后还是原本类型,如果类型不同,则合并后类型为never

interface X{
    q:number,
    w:string
}
interface Y{
    q:boolean,
    r:string,
}
type XY = X&Y

编辑器中直接就给我们了提示,如下图所示:

 高级示例:

interface A {
    inner: D;
}
interface B {
    inner: E;
}
interface C {
    inner: F;
}

interface D {
    d: boolean;
}
interface E {
    e: string;
}
interface F {
    f: number;
}

type ABC = A & B & C;
let abc: ABC = {
    inner: {
        d: false,
        e: 'className',
        f: 5
    }
};

7.2、联合类型

        联合类型和交叉类型比较相似,联合类型通过|符号连接多个类型从而生成新的类型。它主要是取多个类型的交集,即多个类型共有的类型才是联合类型最终的类型。联合类型可以是多个类型其中一个,可做选择,比如:string | number,它的取值可以是string类型也可以是number类型。举几个例子,如下所示:

  • 声明变量的时候设置变量类型
let a:string|number|boolean;
a = 's';
a = 1;
a= false;
  • 多个接口类型进行联合
interface X{
    q:number,
    w:string,
    r:string
}
interface Y{
    q:number
    r:string,
}
type XY = X | Y
let value:XY = {
    q:1,
    r:'r'
}
  • 函数接口类型进行联合
interface X{
    x:()=>string;
    y:()=>Number;
}
interface Y{
    x:()=>string;
}
type XY = X|Y;
function func1():XY{
//此处不进行类型断言为XY在编辑器中会报类型错误
  return {} as XY
 
}
let testFunc = func1();
testFunc.x();
testFunc.y(); //Error:类型“XY”上不存在属性“y”,类型“Y”上不存在属性“y”。

另外我们还要注意,testFunc.x()还会报类型错误,我们需要用类型守卫来区分不同类型。这里我们用in操作符来判断

if('x' in testFunc) testFunc.x()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA | 延迟模型

实际逻辑元器件和它们之间的传输路径都会存在延迟。因此&#xff0c;必须检查设计中的延迟是否满足实际电路的时序约束要求。可以用时序仿真的方法来检查时序&#xff08;timing&#xff09;&#xff0c;即在仿真时向元件或路径中加入和实际相符的延迟信息&#xff0c;并进行相…

4.34、组播(多播)

4.34、多播 1.组播(多播)的介绍①组播地址②如何设置组播&#xff08;组播的使用&#xff09; 2.代码编写①服务端②客户端 1.组播(多播)的介绍 单播地址标识单个 IP 接口&#xff0c;广播地址标识某个子网的所有 IP 接口&#xff0c;多播地址标识一组 IP 接口。单播和广播是寻…

ThreadPoolExecutor原理剖析

1.前言 1.1 为什么要使用线程池&#xff1f; 线程池主要为了解决两个问题 一是当执行大量异步任务时&#xff0c;线程池能够提供较好的性能&#xff0c;避免了重复创建和销毁线程带来的开销二是线程池提供了一种资源限制和管理的手段&#xff0c;比如限制线程个数&#xff0…

【蓝桥杯】最难算法没有之一· 动态规划真的这么好理解?(引入)

欢迎回到&#xff1a;遇见蓝桥遇见你&#xff0c;不负代码不负卿&#xff01; 目录 一、何为动态规划DP 二、记忆化搜索 典例1.斐波那契数列 方法一&#xff1a;暴力递归 方法二&#xff1a;记忆化搜索 变形题 典例2&#xff1a;爬楼梯&#xff08;青蛙跳台阶&#xf…

海睿思分享 | 低而不LOW的低代码开发

低代码&#xff08;Low-Code&#xff09; 是指输出最少的代码&#xff0c;快速完成软件系统开发&#xff0c;进而实现降低开发成本的成效。 Low-Code 中的“Low”与网络热词“LOW”同音同字&#xff0c;前者通常理解为低成本或少量的代码输出&#xff0c;后者是对低认知已见问…

【数据结构】数据结构小试牛刀之单链表

【数据结构】数据结构小试牛刀之单链表 一、目标二、实现1、初始化工作2、单链表的尾插2.1、图解原理2.2、代码实现解答一个疑问 3、单链表的尾删3.1、图解原理3.2、代码实现 4、打印单链表5、单链表的头插5.1、图解原理5.2、代码实现 6、单链表的头删6.1、图解原理6.2、代码实…

【Linux系统】理解Linux中进程间通信

Linux进程间通信 1 进程间通信的介绍1.1为什么要有进程间通信1.2 为什么能进程间通信 2 进程间通信的框架2.1 进程间通信机制的结构2.2 进程间通信机制的类型2.2.1 共享内存式2.2.2 消息传递式 2.3 进程间通信的接口设计 3 进程间通信机制简介4 详细讲解进程间通信部分机制&…

【OAuth2.0 Client 总结】对接github第三方登录以及其他第三方登录总结

之前搞 oauth 登录一直没有搞好&#xff0c;客户端、授权服务端、资源端一起搞对于我刚接触的小菜鸡来说&#xff0c;难度有点大。 然后就先搞了个 Client 端对接 Github 登录。 网上关于 Github 登录的资料有很多&#xff0c;而且框架对 Github 集成的也很好&#xff0c;配置起…

【深入解析K8S专栏介绍】

序言 时间永远是旁观者&#xff0c;所有的过程和结果&#xff0c;都需要我们自己去承担。 Kubernetes (k8s) 是一个容器编排平台&#xff0c;允许在容器中运行应用程序和服务。 专栏介绍 欢迎订阅&#xff1a;【深入解析k8s】专栏 简单介绍一下这个专栏要做的事&#xff1a; 主…

8年测试老兵竟被面试官10分钟pass,这也太难了吧...

前言 随着软件测试领域对于技术要求越来越清晰&#xff0c;到现在测试人员在市场上的岗位需求也变得越来越复杂。极大部分的企业都开始对自动化测试岗位有了更多的需要。自然而然&#xff0c;面试就相对于非常重要了。 笔试部分 1.阐述软件生命周期都有哪些阶段&#xff1f;…

stm32cubemx IAP升级(二)

stm32cubemx IAP升级- App的制作 板卡&#xff1a;Nucleo-L412 平台&#xff1a;macbook pro 工具&#xff1a;vscode stm32cubemx stm32cubeProgramer cmake toolchain 整体思路 将App设置为从0x08007000地址启动&#xff0c;然后初始化一路串口用作接收上位机的升级数据&a…

docker容器:docker镜像的三种创建方法及dockerfile案例

目录 一、基于现有镜像创建 1、创建启动镜像 2、生成新镜像 二、基于本地模板创建 1、OPENVZ 下载模板 2、导入容器生成镜像 三、基于dockerfile创建 1、dockerfile结构及分层 2、联合文件系统 3、docker镜像加载原理 4、dockerfile操作常用的指令 (1)FROM指令 (…

kotlin协程flow retry retryWhen(2)

kotlin协程flow retry retryWhen&#xff08;2&#xff09; 一、retry import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlockingfun main(args: Array<String>) {runBlocking {(1..5).asFlow().onEach {if (it 3) {println("-")throw Runti…

入行IC选择国企、私企还是外企?(内附各IC大厂薪资福利情况)

不少人想要转行IC&#xff0c;但不知道该如何选择公司&#xff1f;下面就来为大家盘点一下IC大厂的薪资和工作情况&#xff0c;欢迎大家在评论区补充。 一&#xff0e;老 牌 巨 头 在 IC 设计领域深耕许久&#xff0c;流程完善、技术扎实&#xff0c;公司各项制度都很完善、前…

关于改造维护工单BAPI_ALM_ORDER_MAINTAIN用于生产订单组件批量修改

1、研究背景 1.1、业务背景 由于销售、研发、工艺等需要频繁变更&#xff0c;导致工单中组件需要频繁的进行变更&#xff0c;修改组件的物料&#xff0c;数量&#xff0c;库存地点&#xff0c;工序等内容。 1.2、技术痛点 为了满足要求&#xff0c;使用了函数&#xff1a;CO…

FVM链的Themis Pro(0x,f4) 5日IDO超百万美元,或让Filecoin逆风翻盘

交易一直是DeFi乃至web3领域最经久不衰的话题&#xff0c;也因此催生了众多优秀的去中心化协议&#xff0c;如Uniswap和Curve。这些协议逐渐成为了整个系统的基石。 在永续合约方面&#xff0c;DYDX的出现将WEB2时代的订单簿带回了web3。其链下交易的设计&#xff0c;仿佛回到了…

为你的软件测试全职业生涯规划保驾护航

目录 前言 1. 软件测试行业现状和未来趋势 2. 从初级测试工程师到高级测试架构师的职业路径 3. 如何提升自身技能和素质 4. 如何管理好自己的职业生涯 总结 前言 作为一名软件测试人员&#xff0c;职业生涯规划是非常重要的。在这篇文章中&#xff0c;我将从以下几个方面…

Mac电脑安装apktool工具包

开发中少不了想看看别人怎么实现某个功能&#xff0c;于是会用到apktool反编译apk&#xff0c;apktool工具包是一个压缩包&#xff0c;下载后无需安装&#xff0c;简单配置下执行命令就能反编译apk了&#xff0c;下面我们看一下如何在Mac OS系统下如何安装apktool工具包&#x…

《Netty》从零开始学netty源码(四十)之SizeClasses

目录 SizeClasses SizeClasses 在netty中&#xff0c;内存会被切割成不同size的块&#xff0c;在分配的时候会根据所需的大小分配相应的内存大小&#xff0c;然而并不是所有的大小都会有相应大小的内存块&#xff0c;比如想要11kb的内存&#xff0c;它并不会确切的给你11kb&am…

Mybatis高级映射及延迟加载

准备数据库表&#xff1a;一个班级对应多个学生。班级表&#xff1a;t_clazz&#xff1b;学生表&#xff1a;t_student 创建pojo&#xff1a;Student、Clazz // Student public class Student {private Integer sid;private String sname;//...... }// Clazz public class Cla…