TS条件类型、断言及名义类型

 文章将讨论处理类型的几个高级模式,包括模拟名义类型的类型烙印、利用条件类型的分配性质在类型层面操作类型,以及安全地扩展原型。

1 函数类型

TS在推导元组的类型时会放宽要求,推导出的结果尽量宽泛,不在乎元组的长度和各位置的类型:

let a = [“hello”,false,1]; // (string | boolean | number)[]

我们有时希望推导结果更严格些,比如上面的类型应该为 [string,boolean,number]。我们可以使用类型断言或者as const来收窄推导结果,我们还可以自定义函数,来实现元组类型收窄:

function tuple<T extends unknown[]>(...t: T) {return t;
}
let t1 = tuple(2,false); // [number,boolean]

1.1 自定义类型防护措施

开发中,我们时常需要判断对象的类型,我们会写一个函数来进行判断:

function isString(input: unknown) {return typeof input === 'string';
}

但是这个函数在某些场景中,可能会达不到我们的预期效果:

function fun(input: string | number) {if (isString(input)) {input.toUpperCase();//报错 Property toUpperCase does not exist on type string | number// Property toUpperCase does not exist on type number}
}

这是因为TS类型细化能力有限,只能细化当前作用域中变量的类型,即上面代码中,TS只能在isString函数中来保证input是个string类型。

TS内置特性:类型防护措施。这是typeof和instanceof细化类型的背后机制。我们通过自定义类型防护措施来实现类型的细化在作用域之间转移:

function isString(a: unknown):a is string {return typeof a === 'string';
}
function fun(input: number | string) {if (isString(input)) {input.toUpperCase();}
}

2 条件类型

条件类型是声明一个依赖类型U和V的类型T,如果 U <: V,就把T赋值给A,否则赋值给B。例如:

type IsString<T> = T extends string ? true : false。

type A = isString<number>; // false

type A = isString<string>; // true

条件类型使用广泛,可以使用类型的地方几乎都能使用条件类型。

type WithoutArrayType<T,U> = T extends U ? never : T[];
type T1 = WithoutArrayType<string | number | boolean, boolean>; // string[] | number[]

2.1 infer 关键字声明泛型

在条件类型中不能适用尖括号(<T>)来声明范型,而是使用infer关键字。

type ElementType<T> = T extends unknown[] ? T[number] : T;
type ElementType2<T> = T extends (infer U)[] ? U : T; // 等效上面type T1 = ElementType<number[]>; // number
type T2 = ElementType2<string[][]>; //string[]

2.2 内置的条件类型

Exclude<T,U>: 返回在T中而不在U中的类型。

type ExcludeType = Exclude<string | number | boolean, number>; // string | boolean

Extract<T,U>: 返回T中可赋值给U的类型。

type ExtractType = Extract< number | boolean, string | number | 'x'>; // number

NonNullable<T>: 从T中排出null和undefined。

type A = { a? :number,b: string | null};
type aType = NonNullable<A["a"]>; // number
type A1 = NonNullable<A>; // A & {}let a1:A1 = {a:1,b:1};
let a2:A1 = {}; // Property b is missing in type {} but required in type A
let a3:A1 = {b:null};

ReturnType<F>: 计算函数的返回类型(不适用泛型和重载的函数)。

type Fun = () => string;
type T1 = ReturnType<Fun>; // string

InstanceType<C>: 返回类构造方法的实例类型。

type CustomClass = {new(): C}
type C = {name:"user"};
type T = InstanceType<CustomClass>; // {name: "user"}

3 断言

在没有足够时间把所有类型都规划好,但是我们希望TS不要因为这个而让我们程序运行不起来,我们可以使用断言的方式(但要尽量少用,如果大量依赖这些,说明项目需要重构了)。

1)类型断言。有两种方式:as 和尖括号。

function fun(input: string | number) {let str = input as string;// stringlet str2 = <string> input; // string
}

2)非空断言。

type Student = {id?: number};
let s:Student = {};
let s2: Student = {id: 2};
function fun2(id: number) {}
fun2(s.id!);
fun2(s2.id!);
fun2(s2.id); // 报错 Argument of type 'number | undefined' is not assignable to parameter of type 'number'.

3)明确赋值断言。TS通过明确赋值检查确保使用变量时已经为其赋值。

我们在定义一个变量时,如果在没有为其赋值而使用它,那么TS将报错。

class Student{name: stringshowName() {console.log(this.name);}
}let student = new Student();
student.showName(); // 报错  Property 'name' has no initializer and is not definitely assigned in the constructor.

明确赋值,是指在变量名后面加个”!“符号,来告诉TS,我已经确保,在使用这个变量之前,我已经为它赋值。

class Student{name!: stringshowName() {console.log(this.name);}
}let student = new Student();
student.showName(); // undefined

4 名义类型

TS 是结构化类型不是名义类型系统,即TS只根据类型结构来判断类型而不是类型名称。而在Java中,是根据类名来确定类型的。比如Man 和 People,在TS中,如果这两个类型的结构是一样的,那么它们是同一个类型,而在Java中,因为它们名字不同,就注定它们不是同一个类型。

虽然在TS中,通过结构类型给予了开发者很多方便,但是有时名义类型能发挥的作用是结构类型很难替代的:

定义一个函数,要求参数为一个StudentID类型的字符串。而这个StudentID本质上也是字符串类型。

我们可以使用类型烙印技术模拟实现。

4.1 类型烙印

类型烙印即给特定类型一个唯一结构,来让它从其他相似类型结构中区别开。

type StudentID = string & {readonly brand: unique symbol};
function StudentID(id:string) {return id as StudentID;
}function showStudentId(id: StudentID) {}let id = StudentID("123"); // StudentID
showStudentId(id);
showStudentId("123"); // 报错  Argument of type string is not assignable to parameter of type StudentID
//Type string is not assignable to type { readonly brand: unique symbol; }

类型烙印降低了运行时的开销,是一种编译时结构。

多数应用没必要使用这个,不过,对于大型应用或者处理容易混淆的类型时,带烙印的类型可以极大地提升程序的安全性。

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

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

相关文章

Spring Cloud Gateway使用和配置

Spring Cloud Gateway是Spring官方基于Spring 5.0&#xff0c;Spring Boot 2.0和Project Reactor等技术开发的网关&#xff0c;Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关&#xff…

Linux 基础IO

文章目录 前言基础IO定义系统IO接口文件描述符重定向原理缓冲区刷新 前言 要知道每个函数/接口的全部参数和返回值建议去官网或者直接在Linux的man手册中查&#xff0c;这不是复制粘贴函数用法的文章。 C语言文件读写介绍链接 基础IO定义 IO是Input/Output的缩写&#xff0c…

optional

参考资料&#xff1a; Java8 Optional用法和最佳实践 - 掘金 一、背景 根据Oracle文档&#xff0c;Optional是一个容器对象&#xff0c;可以包含也可以不包含非null值。Optional在Java 8中引入&#xff0c;目的是解决 NullPointerExceptions的问题。本质上&#xff0c;Optio…

2024年网络安全竞赛-网站渗透

网站渗透 (一)拓扑图 1.使用渗透机对服务器信息收集,并将服务器中网站服务端口号作为flag提交; 使用nmap工具对靶机进行信息收集 2.使用渗透机对服务器信息收集,将网站的名称作为flag提交; 访问页面即可 3.使用渗透机对服务器渗透,将可渗透页面的名称作为flag提交…

Python:核心知识点整理大全5-笔记

目录 2. 使用方法pop()删除元素 3. 弹出列表中任何位置处的元素 4. 根据值删除元素 3 章 列表简介 3.3 组织列表 3.3.1 使用方法 sort()对列表进行永久性排序 3.3.2 使用函数 sorted()对列表进行临时排序 3.3.3 倒着打印列表 3.3.4 确定列表的长度 3.5 小结 2. 使用方…

软件测试:测试用例八大要素模板

一、通用测试用例八要素 1、用例编号&#xff1b; 2、测试项目&#xff1b; 3、测试标题&#xff1b; 4、重要级别&#xff1b; 5、预置条件&#xff1b; 6、测试输入&#xff1b; 7、操作步骤&#xff1b; 8、预期输出 二、具体分析通用测试用例八要素 1、用例编号 一般是数字…

[NAND Flash 2.1] NAND Flash 闪存改变了现代生活

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< ​ 1989年NAND闪存面世了&#xff0c;它曾经且正在改变了我们的日常生活。 NAND 闪存发明之所以伟大&#xff0c;是因为&#xff0c…

一个CV算法工程师在技术方面的小反思

极市导读 正如作者所说,做一个算法工程师最重要的素质是在海量的算法方案中理解,吃透那些真正的干货,然后不断在实践中去验证,并总结吸收到自己的脑子里。本文记录了作者在算法工程师这个岗位上一年后总结的一些关于技术上的经验总结。>>加入极市CV技术交流群,走在计…

怎样解决编译后的exe文件运行时产生的错误?

编译后的exe文件运行时&#xff0c;错误如下错误提示&#xff1a;Traceback (most recent call last):File "pd.py", line 1, in <module>from pdf2docx import parse ModuleNotFoundError: No module named pdf2docx 怎样解决&#xff1f; 这个错误提示表明…

java数据结构面试题

1.栈和队列的共同特点是&#xff08;只允许在端点处插入和删除元素&#xff09; 4.栈通常采用的两种存储结构是&#xff08;线性存储结构和链表存储结构&#xff09; 5.下列关于栈的叙述正确的是&#xff08;D&#xff09; A.栈是非线性结构 B.栈是一种树状结构 C.栈具有先进先…

苹果OS X系统介绍(Mac OS --> Mac OS X --> OS X --> macOS)

文章目录 OS X系统介绍历史与版本架构内核与低级系统图形&#xff0c;媒体和用户界面应用程序和服务 特性用户友好强大的命令行安全性集成与互操作性 总结 OS X系统介绍 OS X是由苹果公司为Macintosh计算机系列设计的基于UNIX的操作系统。其界面友好&#xff0c;易于使用&…

使用 nohup java - jar 不输出日志

要在使用nohup java -jar命令时不输出日志&#xff0c;可以将标准输出和标准错误输出重定向到特殊设备文件/dev/null。这样做将会丢弃所有的输出。 以下是在Linux中使用nohup java -jar命令并禁止输出日志的示例&#xff1a; 复制代码 nohup java -jar your-application.jar …

Python可视化(二)——Seaborn

Seaborn是一个基于matplotlib的可视化库&#xff0c;其为用户提供了高级接口&#xff0c;并且该工具还深度集成了pandas的数据结构。并且该工具该集成了很多数据库&#xff0c;配合官网给出的代码示例&#xff0c;可以更方便的进行操作。 官网对它的介绍为&#xff1a; Seabo…

Servlet学习笔记

简介 浏览器请求处理流程&#xff1a;浏览器发请求 > 服务器tomcat( > 应用程序 ( > servlet) ) Servlet应用的三大作用域&#xff1a;request&#xff0c;session&#xff0c;application tomcat存放项目的层级结构 注释&#xff1a;servlet原引用包名 javax.serv…

卡尔曼滤波器

欢迎访问我的博客首页。 卡尔曼滤波器 1. 参考 1. 参考 卡尔曼滤波器&#xff0c;B 站&#xff0c;2020。扩展卡尔曼滤波器&#xff0c;CSDN&#xff0c;2023。

Git的安装以及SSH配置

前言 近期工作需要&#xff0c;所以版本管理工具要用到Git&#xff0c;某些操作需要ssh进行操作&#xff0c;在某次操作中遇到&#xff1a;git bash报错&#xff1a;Permission denied, please try again。经排查是ssh没有配置我的key&#xff0c;所以就借着这篇文章整理了一下…

WorkPlus即时通讯,让沟通零障碍!企业协作更高效

如今&#xff0c;随着信息技术的快速发展&#xff0c;企业对于高效沟通和即时协作的需求也日益增长。在这个数字化时代&#xff0c;WorkPlus作为一款领先的企业级移动办公平台&#xff0c;以其强大的即时通讯功能和卓越的用户体验&#xff0c;成功为企业打造了高效沟通的新时代…

input = torch.randn(20, 2, 11, 11, 32)输出形式

input torch.randn(20, 2, 11, 11, 32) m torch.nn.AdaptiveAvgPool3d((1,1, 32)) xm(input) print(x.shape) 结果&#xff1a; 也就是不用管批次和通道数

pico示波器使用

文章目录 Pico示波器保存波形Pico示波器录制数据Pico示波器解析CAN报文Pico示波器保存波形 Pico示波器可以通过以下步骤保存波形: 在示波器上选择要保存的波形。连接示波器到计算机上,可以使用USB或者Ethernet连接。打开PicoScope软件,选择“File”菜单,然后选择“Save As…

Python开发运维:Python垃圾回收机制

目录 一、理论 1.Python垃圾回收机制 一、理论 1.Python垃圾回收机制 &#xff08;1&#xff09;引⽤计数器 1&#xff09;环状双向链表 refchain 在python程序中创建的任何对象都会放在refchain链表中。 name "david" age 20 hobby ["篮球",游泳…