TypeScript中的类型运算符
类型运算符
1. keyof运算符
1. 简介
是一个单目运算符,接受一个对象类型作为参数,返回该对象的所有键名组成的联合类型。
type MyObj = {foo: number,bar: string,
};type Keys = keyof MyObj; // 'foo'|'bar'
这个例子keyof MyObj返回MyObj的所有键名组成的联合类型,即'foo'|'bar'
由于 JavaScript 对象的键名只有三种类型,所以对于任意对象的键名的联合类型就是string|number|symbol。
对于没有自定义键名的类型使用 keyof 运算符,返回never类型,表示不可能有这样类型的键名
type KeyT = keyof object; // never
上面示例中,由于object类型没有自身的属性,也就没有键名,所以keyof object返回never类型。
由于 keyof 返回的类型是string|number|symbol,如果有些场合只需要其中的一种类型,那么可以采用交叉类型的写法。
type Capital<T extends string> = Capitalize<T>;type MyKeys<Obj extends object> = Capital<keyof Obj>; // 报错
type MyKeys<Obj extends object> = Capital<string & keyof Obj>;
这个列子中,string & keyof Obj等同于string & string|number|symbol进行交集运算,最后返回string,因此Capital<T extends string>就不会报错了。
如果对象属性名采用索引形式,keyof 会返回属性名的索引类型。
// 示例一
interface T {[prop: number]: number;
}// number
type KeyT = keyof T;// 示例二
interface T {[prop: string]: number;
}// string|number
type KeyT = keyof T;
上面的示例二,keyof T返回的类型是string|number,原因是 JavaScript 属性名为字符串时,包含了属性名为数值的情况,因为数值属性名会自动转为字符串
如果 keyof 运算符用于数组或元组类型,得到的结果可能出人意料。
type Result = keyof ['a', 'b', 'c'];
// 返回 number | "0" | "1" | "2"
// | "length" | "pop" | "push" | ···
上面示例中,keyof 会返回数组的所有键名,包括数字键名和继承的键名。
对于联合类型,keyof 返回成员共有的键名。
type A = { a: string; z: boolean };
type B = { b: string; z: boolean };// 返回 'z'
type KeyT = keyof (A | B);
对于交叉类型,keyof 返回所有键名。
type A = { a: string; x: boolean };
type B = { b: string; y: number };// 返回 'a' | 'x' | 'b' | 'y'
type KeyT = keyof (A & B);// 相当于
keyof (A & B) ≡ keyof A | keyof B
keyof 取出的是键名组成的联合类型,如果想取出键值组成的联合类型,可以像下面这样写。
type MyObj = {foo: number,bar: string,
};type Keys = keyof MyObj;type Values = MyObj[Keys]; // number|string
上面示例中,Keys是键名组成的联合类型,而MyObj[Keys]会取出每个键名对应的键值类型,组成一个新的联合类型,即number|string。
2. keyof运算符的用途
- 往往用于精确表达对象的属性类型
- 用于属性映射,即将一个类型的所有属性逐一映射成其他值
2. in运算符
在js中in用来确定对象是否包含某个属性名,在ts 类型运算中,in运算符用来取出(遍历)联合类型的每一个成员类型。
type U = 'a'|'b'|'c';type Foo = {[Prop in U]: number;
};
// 等同于
type Foo = {a: number,b: number,c: number
};
[Prop in U]表示依次取出联合类型U的每一个成员。
3. 方括号运算符
用来取出对象的键值类型,比如T[K]会返回对象T的属性K的类型。
type Person = {age: number;name: string;alive: boolean;
};
// Age 的类型是 number
type Age = Person['age'];
方括号的参数如果是联合类型,那么返回的也是联合类型。
type Person = {age: number;name: string;alive: boolean;
};// number|string
type T = Person['age'|'name'];// number|string|boolean
type A = Person[keyof Person];
如果访问不存在的属性,会报错。
type T = Person['notExisted']; // 报错
方括号运算符的参数也可以是属性名的索引类型。
type Obj = {[key:string]: number,
};// number
type T = Obj[string];
这个语法对于数组也适用,可以使用number作为方括号的参数。
// MyArray 的类型是 { [key:number]: string }
const MyArray = ['a','b','c'];// 等同于 (typeof MyArray)[number]
// 返回 string
type Person = typeof MyArray[number];
上面示例中,MyArray是一个数组,它的类型实际上是属性名的数值索引,而typeof MyArray[number]的typeof运算优先级高于方括号,所以返回的是所有数值键名的键值类型string。
方括号里面不能有值的运算。
// 示例一
const key = 'age';
type Age = Person[key]; // 报错// 示例二
type Age = Person['a' + 'g' + 'e']; // 报错
上面两个示例,方括号里面都涉及值的运算,编译时不会进行这种运算,所以会报错。
4. extends…?:条件运算符
可以根据当前类型是否符合某种条件,返回不同的类型。
T extends U ? X : Y
上面式子中的extends用来判断,类型T是否可以赋值给类型U,即T是否为U的子类型,这里的T和U可以是任意类型。如果T能够赋值给类型U,表达式的结果为类型X,否则结果为类型Y。
// true
type T = 1 extends number ? true : false;
上面示例中,1是number的子类型,所以返回true。
如果需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型。
(A|B) extends U ? X : Y// 等同于(A extends U ? X : Y) |
(B extends U ? X : Y)
上面示例中,A|B是一个联合类型,进行条件运算时,相当于A和B分别进行运算符,返回结果组成一个联合类型。
如果不希望联合类型被条件运算符展开,可以把extends两侧的操作数都放在方括号里面。
// 示例一
type ToArray<Type> =Type extends any ? Type[] : never;// string[]|number[]
type T = ToArray<string|number>;// 示例二
type ToArray<Type> =[Type] extends [any] ? Type[] : never;// (string | number)[]
type T = ToArray<string|number>;
上面的示例一,传入ToArray<Type>的类型参数是一个联合类型,所以会被展开,返回的也是联合类型。示例二是extends两侧的运算数都放在方括号里面,所以传入的联合类型不会展开,返回的是一个数组。
条件运算符还可以嵌套使用。
type LiteralTypeName<T> =T extends undefined ? "undefined" :T extends null ? "null" :T extends boolean ? "boolean" :T extends number ? "number" :T extends bigint ? "bigint" :T extends string ? "string" :never;// "bigint"
type Result1 = LiteralTypeName<123n>;// "string" | "number" | "boolean"
type Result2 = LiteralTypeName<true | 1 | 'a'>;
上面示例是一个多重判断,返回一个字符串的值类型,对应当前类型。
5. infer关键字
用来定义泛型里面推断出来的类型参数,而不是外部传入的类型参数。它通常跟条件运算符一起使用,用在extends关键字后面的父类型中。
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
上面示例中,infer Item表示Item这个参数是 TypeScript 自己推断出来的,不用显式传入,而Flatten<Type>则表示Type这个类型参数是外部传入的。Type extends Array<infer Item>则表示,如果参数Type是一个数组,那么就将该数组的成员类型推断为Item,即Item是从Type推断出来的。
一旦使用Infer Item定义了Item,后面的代码就可以直接调用Item了。下面是上例的泛型Flatten<Type>的用法。
// string
type Str = Flatten<string[]>;// number
type Num = Flatten<number>;
上面示例中,第一个例子Flatten<string[]>传入的类型参数是string[],可以推断出Item的类型是string,所以返回的是string。第二个例子Flatten<number>传入的类型参数是number,它不是数组,所以直接返回自身。
如果不用infer定义类型参数,那么就要传入两个类型参数。
type Flatten<Type, Item> =Type extends Array<Item> ? Item : Type;
上面是不使用infer的写法,每次调用Flatten的时候,都要传入两个参数,就比较麻烦。
下面的例子使用infer,推断函数的参数类型和返回值类型。
type ReturnPromise<T> =T extends (...args: infer A) => infer R ? (...args: A) => Promise<R> : T;
上面示例中,如果T是函数,就返回这个函数的 Promise 版本,否则原样返回。infer A表示该函数的参数类型为A,infer R表示该函数的返回值类型为R。
如果不使用infer,就不得不把ReturnPromise<T>写成ReturnPromise<T, A, R>,这样就很麻烦,相当于开发者必须人肉推断编译器可以完成的工作。
下面是infer提取对象指定属性的例子。
type MyType<T> =T extends {a: infer M,b: infer N} ? [M, N] : never;// 用法示例
type T = MyType<{ a: string; b: number }>;
// [string, number]
上面示例中,infer提取了参数对象的属性a和属性b的类型。
下面是infer通过正则匹配提取类型参数的例子。
type Str = 'foo-bar';type Bar = Str extends `foo-${infer rest}` ? rest : never // 'bar'
上面示例中,rest是从模板字符串提取的类型参数。
6. is运算符
函数返回布尔值时,可以使用is运算符,来限定返回值与参数之间的关系。
is运算符用来描述返回值是true还是false。
function isFish(pet: Fish|Bird
):pet is Fish {return (pet as Fish).swim !== undefined;
}
上面示例中,函数isFish()的返回值类型为pet is Fish,表示如果参数pet类型为Fish,则返回true,否则返回false。
is运算符总是用于描述函数的返回值类型,写法采用parameterName is Type的形式,即左侧为当前函数的参数名,右侧为某一种类型。它返回一个布尔值,表示左侧参数是否属于右侧的类型。
is运算符可以用于类型保护。
function isCat(a:any): a is Cat {return a.name === 'kitty';
}let x:Cat|Dog;if (isCat(x)) {x.meow(); // 正确,因为 x 肯定是 Cat 类型
}
上面示例中,函数isCat()的返回类型是a is Cat,它是一个布尔值。后面的if语句就用这个返回值进行判断,从而起到类型保护的作用,确保x是 Cat 类型,从而x.meow()不会报错(假定Cat类型拥有meow()方法)
is运算符还有一种特殊用法,就是用在类(class)的内部,描述类的方法的返回值。
class Teacher {isStudent():this is Student {return false;}
}class Student {isStudent():this is Student {return true;}
}
上面示例中,isStudent()方法的返回值类型,取决于该方法内部的this是否为Student对象。如果是的,就返回布尔值true,否则返回false。
注意,this is T这种写法,只能用来描述方法的返回值类型,而不能用来描述属性的类型。
7. 模板字符串
ts可以使用模板字符串构建类型,模板字符串最大的特点就是内部可以引用其他类型。
type World = "world";// "hello world"
type Greeting = `hello ${World}`;
上面示例中,类型Greeting是一个模板字符串,里面引用了另一个字符串类型world,因此Greeting实际上是字符串hello world
模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用这6种以外的类型会报错。
模板字符串里面引用的类型,如果是一个联合类型,那么它返回的也是一个联合类型,即模板字符串可以展开联合类型。
type T = 'A'|'B';// "A_id"|"B_id"
type U = `${T}_id`;
上面示例中,类型U是一个模板字符串,里面引用了一个联合类型T,导致最后得到的也是一个联合类型。
如果模板字符串引用两个联合类型,它会交叉展开这两个类型。
type T = 'A'|'B';type U = '1'|'2';// 'A1'|'A2'|'B1'|'B2'
type V = `${T}${U}`;
上面示例中,T和U都是联合类型,各自有两个成员,模板字符串里面引用了这两个类型,最后得到的就是一个4个成员的联合类型。
8. satisfies运算符
!!!后期补
相关文章:
TypeScript中的类型运算符
类型运算符 1. keyof运算符 1. 简介 是一个单目运算符,接受一个对象类型作为参数,返回该对象的所有键名组成的联合类型。 type MyObj {foo: number,bar: string, };type Keys keyof MyObj; // foo|bar这个例子keyof MyObj返回MyObj的所有键名组成的…...
【蓝桥杯选拔赛真题03】C++输出字母Y 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析
目录 C/C++输出字母Y 一、题目要求 1、编程实现 2、输入输出 二、算法分析...
redis搭建集群-多实例快速搭建
1.基础的redis.conf的配置 # Redis configuration file example. # # Note that in order to read the configuration file, Redis must be # started with the file path as first argument: # # ./redis-server /path/to/redis.conf# Note on units: when memory size is ne…...
为什么进行压力测试? 有哪些方法?
在信息技术飞速发展的今天,软件系统的性能已经成为了用户满意度的决定性因素之一。而要确保一个系统在实际使用中能够稳定可靠地运行,压力测试就显得尤为关键。本文将深入探讨什么是压力测试,为什么它是如此重要,以及一些常见的压…...
Java开发者必备:支付宝沙箱环境支付远程调试指南
🔥博客主页: 小羊失眠啦. 🔖系列专栏: C语言、Linux、Cpolar ❤️感谢大家点赞👍收藏⭐评论✍️ 文章目录 前言1. 下载当面付demo2. 修改配置文件3. 打包成web服务4. 局域网测试5. 内网穿透6. 测试公网访问7. 配置二级…...
基于STM32温湿度传感器采集报警系统设计
**单片机设计介绍,1648【毕设课设】基于STM32温湿度传感器采集报警系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序程序 六、 文章目录 一 概要 这次的设计主要是通过读取DHT11和HCSR04的数值,(Proteus的传感器…...
檢測項目簡體字
某些項目可能要求代碼中不允許使用簡體字 安裝stcheck檢查 yarn add stcheck --dev在項目根目錄創建 st.config.json 文件 {"patterns": ["./**/*.(ts|js|tsx|jsx|vue|html)","!**/node_modules/**","!.git/**"],"gitignore&q…...
适用于嵌入式arm的ffmpeg编解码
在嵌入式arm应用开发中,经常会遇到需要处理视频的情况,这时候就需要强大的开源工具ffmpeg出马了。 这里可以下载到各个版本的ffmpeg。 ffmpeg各版本https://www.videohelp.com/software/ffmpeg/old-versions 现在ffmpeg更新较频繁,如…...
nlp与知识图谱代码解读_词嵌入
目录 词嵌入简单原理代码案例解读专业原理介绍场景 词嵌入 简单原理 可以使用一些比喻和生活中的例子: 老师: 你们还记得玩乐高积木的时候,每个积木块代表了一个特定的事物或形状吗?现在,想象一下,每个词…...
HarmonyOS 音频通话开发指导
常用的音频通话模式包括 VOIP 通话和蜂窝通话。 ● VOIP 通话:VOIP(Voice over Internet Protocol)通话是指基于互联网协议(IP)进行通讯的一种语音通话技术。VOIP 通话会将通话信息打包成数据包,通过网络进…...
LeetCode讲解篇之面试题 01.08. 零矩阵
文章目录 题目描述题解思路题解代码 题目描述 题解思路 遍历矩阵,若当前元素为零,则将该行和该列的第一个元素置零 遍历第一行,若当前元素为零,则将当前列置零 遍历第一列,若当前元素为零,则将当前行置零 …...
安装python虚拟环境
什么是虚拟环境: 虚拟环境的意义,就如同 虚拟机 一样,它可以实现不同环境中Python依赖包相互独立,互不干扰。 环境准备 安装python (到官网下载Download Python配置环境变量,cmd进入命令行输入 python…...
【App 抓包提示网络异常怎么破?】
背景 当你测试App的时候,想要通过Fiddler/Charles等工具抓包看下https请求的数据情况,发现大部分的App都提示网络异常/无数据等等信息。以“贝壳找房”为例: 455 x 705 Fiddler中看到的请求是这样的: 619 x 215 你可能开始找证书的问题:是不是Fiddler/Charles的证书没有…...
【开发篇】一、处理函数:定时器与定时服务
文章目录 1、基本处理函数2、定时器和定时服务3、KeyedProcessFunction下演示定时器4、process重获取当前watermark 前面API篇完结,对数据的转换、聚合、窗口等,都是基于DataStream的,称DataStreamAPI,如图: 在Flink…...
重入漏洞EtherStore
重入漏洞 // SPDX-License-Identifier: MIT pragma solidity ^0.8.13;contract EtherStore {mapping(address > uint) public balances;function deposit() public payable {balances[msg.sender] msg.value;}function withdraw() public {uint bal balances[msg.sender]…...
账号运营的底层逻辑---获客思维
什么是运营? 运营是做什么的? 什么是内容运营? 什么是活动运营? 一篇带你搞清楚所有的底层逻辑!...
Pinia中如何实现数据持久化操作
使用vue3中的pinia,我们可以在多个页面间共享数据,但是一旦我们关闭或刷新页面,这些数据就会丢失,因此,我们需要有一种数据持久化的解决方案。在记录vue3 使用vue3中的pinia,我们可以在多个页面间共享数据&…...
【owt-server】RTC视频接收调用流程学习笔记1: Call::CreateVideoReceiveStream 前后
WebRTC源码分析——Call模块 大神提到,call模块是在worker线程创建的。主要创建接收、发送流Call模块是WebRTC会话中不可缺少的一个模块,一个Call对象可以包含多个发送/接收流,且这些流对应同一个远端端点,并共享码率估计。 call中通过webrtc::VideoReceiveStream::Config …...
淘宝商品链接获取淘宝商品评论数据(用 Python实现淘宝商品评论信息抓取)
在网页抓取方面,可以使用 Python、Java 等编程语言编写程序,通过模拟 HTTP 请求,获取淘宝多网站上的商品详情页面评论内容。在数据提取方面,可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#…...
十九、类型信息(1)
本章概要 为什么需要 RTTI RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息 RTTI 把我们从只能在编译期进行面向类型操作的禁锢中解脱了出来,并且让我们可以使用某些非常强大的程序。对 RTTI …...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
