你会用 TypeScript 的条件类型吗?
我们可以使用 TypeScript 中的条件类型来根据逻辑定义某些类型,就像是在编写代码那样。它采用的语法和我们在 JavaScript 中熟悉的三元运算符很像:condition ? ifConditionTrue : ifConditionFalse。我们来看看他是怎么工作的。
TypeScript 的条件类型使用方式
假设我们有一个值,这个值可以表示用户的出生日期或者年龄。
如果是出生日期,那他的类型应该是 string。
如果是年龄,那他的类型是 number。
我们来定义这三种类型。
type Dob = string;
type Age = number;
type UserAgeInformation<T> = T extends number ? number : string;
Dob 和 Age 不需要多解释,我们来解释一下 UserAgeInformation。它接受一个泛型,可以是任何类型。如果 T extends number 为 true,就意味着传入的类型是 number 类型,我们就把 UserAgeInformation 设置为 number 类型。否则的话就设置为 string 类型。我们在使用的时候可以这样:
let userAge:UserAgeInformation<Age> = 100;
let userDob:UserAgeInformation<Dob> = '12/12/1945';
条件类型和 keyof 组合
除了上面介绍的用法,我们还可以通过检查是否扩展了一个对象来更进一步。比如,假设我们的客户有两种类型:Horse 和 User。两种类型的客户都有 age、name 两个字段。User 类型的客户还有一个 address 字段,它表示了详细的地址。Horse 类型的客户有 location 两个字段,它只是表示一个大概的位置。我们来定义这几种类型:
type User = {age: number,name: string,address: string
}type Horse = {age: number,name: string
}type UserAddress = {addressLine1: string,city: string,country: string,
}type HorseAddress = {location: 'farm' | 'savanna' | 'field' | 'other'
}
在未来,我们还可能会有其他类型的客户,所以我们可以通过检查 T 是否具有 address 属性,如果有,我们使用 UserAddress 类型,否则使用 HorseAddress 类型。
type AddressComponents<T> = T extends { address: string } ? UserAddress : HorseAddresslet userAddress:AddressComponents<User> = {addressLine1: "123 Fake Street",city: "Boston",country: "USA"
}let horseAddress:AddressComponents<Horse> = {location: 'farm'
}
T extends { address: string } 的含义是检查 T 是否具有 address 属性。
在条件返回中使用 T
在三元表达式的条件返回中,我们也可以使用 T。
比如下面的例子:
type User = {age: number,name: string,address: string
}type Horse = {age: number,name: string
}type UserType<T> = T extends { address: string } ? T : Horselet myUser: UserType<User> = {age: 104, name: "John Doe",address: "123 Fake Street"
}
T 被定义为 User,当我们调用 UserType 时,myUser 的类型就是 User,并且需要具有这种类型中所定义的字段。
在类型输出中使用 T 时的联合类型
如果我们在这里传递一个联合类型:
type UserType<T> = T extends { address: string } ? T : stringlet myUser: UserType<User | Horse> = {age: 104, name: "John Doe",address: "123 Fake Street"
}
myUser 的类型会变成是 User|string,因为 User 通过了条件检测,但是 Horse 没有通过,所以它的类型是字符串。
如果我们以某种方式修改 T,比如把它设置为数组。所有 T 的值都会被单独修改。
type User = {age?: number,name: string,address?: string
}
type Horse = {age?: number,name: string
}
// 如果 T 包含类型是 string 的 name 属性,就会返回 T[]
type UserType<T> = T extends { name: string } ? T[] : never;// myUser 的类型是 User[]|Horse[],因为 User 和 Horse 都具有 name 属性
let myUser:UserType<User | Horse> = [{ name: "John" }, { name: "Horse" }]
在这里我们已经简化了 User 和 Horse,它们只留下了必须需要的 name 字段。在条件类型中,两种类型都包含了 name。所以两者都会返回 true,并且返回的类型是 T[],由于两者都返回 true,所以 myUser 的类型是 User[]|Horse[],所以我们可以简单地提供一个包含 name 属性的对象数组。这种行为通常很好,但是在某些情况下,我们希望返回一个数组。在这种情况下,如果我们想避免这样分布类型,可以在 周围添加 { name: string }。
type User = {age?: number,name: string,address?: string
}
type Horse = {age?: number,name: string
}
// 我们避免分布类型,因为 T 和 { name: string} 都在方括号中
type UserType<T> = [T] extends [{ name: string }] ? T[] : never;// 这样现在的类型不同了,它是 (User|Horse)[]
let myUser:UserType<User | Horse> = [{ name: "John" }, { name: "Horse" }]
通过使用方括号,我们的类型已经转为 (User|Horse)[],而不是 User[]|Horse[]。这在某些特殊的场景中很有用。但是条件类型会增加复杂性,不可以滥用。
使用条件类型推断类型
我们也可以在使用条件时使用 infer 关键字。假设我们有两种类型,一种用于数字数组,另一种用于字符串数组。在这个例子中,infer 将会推断数组中每个项目的类型,并返回正确的类型:
type StringArray = string[]
type NumberArray = number[]
type MixedArray = number[] | string[]
type ArrayType<T> = T extends Array<infer Item> ? Item: never// 因为 NumberArray 中项目的类型是 number,所以 myItem1 是 number 类型
let myItem1: ArrayType<NumberArray> = 45
// 因为 StringArray 中项目的类型是 string,所以 myItem2 是 string 类型
let myItem2: ArrayType<StringArray> = 'string'
// 因为 MixedArray 中项目的类型是 number|string,所以 myItem3 是 number|string 类型
let myItem3: ArrayType<MixedArray> = 'string'
我们在条件类型中定义了一个新的参数 Item,它是 extends Array 中的子项 T。但是我们必须传入数组,它才会有效,因为我们使用的是 Array。如果 T 不是数组,那么 ArrayType 的类型将会是 never。
总结
如果你刚接触到 TypeScript 的条件类型,你可能会觉得很疑惑。但是它解决了某些特定情况下编写类型比较复杂的一种解决方式。如果你在某个项目中看到它,或者简化你想你的项目代码,它或许很有用。
相关文章:
你会用 TypeScript 的条件类型吗?
我们可以使用 TypeScript 中的条件类型来根据逻辑定义某些类型,就像是在编写代码那样。它采用的语法和我们在 JavaScript 中熟悉的三元运算符很像:condition ? ifConditionTrue : ifConditionFalse。我们来看看他是怎么工作的。 TypeScript 的条件类型…...
云原生丨一文教你基于Debezium与Kafka构建数据同步迁移(建议收藏)
文章目录前言一、安装部署Debezium架构部署示意图安装部署二、数据迁移Postgres迁移到PostgresMySQL迁移到PostgresSQL前言 在项目中,我们遇到已有数据库现存有大量数据,但需要将全部现存数据同步迁移到新的数据库中,我们应该如何处理呢&…...
顶象APP加固的“蜜罐”技术有什么作用
目录 蜜罐有很多应用模式 蜜罐技术让App加固攻守兼备 顶象端加固的三大功能 为了捕获猎物,猎人会在设置鲜活的诱饵。被诱惑的猎物去吃诱饵时,就会坠入猎人布置好的陷阱,然后被猎人擒获,这是狩猎中常用的一种手段。在业务安全防…...
训练一个ChatGPT需要多少数据?
“风很大”的ChatGPT正在席卷全球。作为OpenAI在去年底才刚刚推出的机器人对话模型,ChatGPT在内容创作、客服机器人、游戏、社交等领域的落地应用正在被广泛看好。这也为与之相关的算力、数据标注、自然语言处理等技术开发带来了新的动力。自OpenAI发布ChatGPT以来&…...
【GlobalMapper精品教程】053:打开dbf文件并生成有坐标系的shp数据
本文讲解在globalmapper汇总打开dbf文件并生成有坐标系的shp数据。 文章目录一、dbf文件解读二、打开dbf文件二、另存为shp文件一、dbf文件解读 我们可以通过Excel或FME等多种软件查看dbf的结构,字段有:Name,kind,Lat,…...
图像亮度调整
非线性方式 调整图像的方法有很多,最常用的方法就是对图像像素点的R、G、B三个分量同时进行增加(减少)某个值,达到调整亮度的目的。即改变图像的亮度,实际就是对像素点的各颜色分量值做一个平移。这种方法属于非线性的…...
精简版SDL落地实践
一、前言一般安全都属于运维部下面,和上家公司的运维总监聊过几次一些日常安全工作能不能融入到DevOps中,没多久因为各种原因离职。18年入职5月一家第三方支付公司,前半年在各种检查中度过,监管形势严峻加上大领导对安全的重视(主…...
第一回:Matplotlib初相识
一、认识matplotlib Matplotlib是一个Python 2D绘图库,能够以多种硬拷贝格式和跨平台的交互式环境生成出版物质量的图形,用来绘制各种静态,动态,交互式的图表。 Matplotlib可用于Python脚本,Python和IPython Shell、…...
怎么找回电脑删除的图片
怎么找回电脑删除的图片?图片作为一种非常简单方便的文件,经常被用来辅助我们的日常工作和学习。但在我们整理电脑时,如果我们不小心手一抖就删除了一些重要的图片,遇到这种事我们要如何才能恢复呢? 众所周知,简单的删除并不会完…...
【Linux】进程状态与进程优先级
目录一.进程状态1.阻塞:2.挂起:具体情况3.具体操作系统状态变化R:运行状态(running)S:休眠状态(sleeping)D:磁盘休眠状态(Disk sleep)T:暂停状态(stopped)暂停进程继续进程t:追踪暂停状态(traci…...
Python+Qt生日提醒
PythonQt生日提醒如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助!前言这篇博客针对<<PythonQt生日提醒>>编写代码,代码整洁,规则,易读。 学习与应用推荐首选。文章目…...
第二章 编写MBR主引导记录
主引导记录(MBR,Master Boot Record)是采用MBR分区表的硬盘的第一个扇区,即C/H/S地址的0柱面0磁头1扇区,也叫做MBR扇区 计算机的启动过程 为什么程序要载入内存 CPU的硬件电路被设计成只能运行处于内存中的程序&…...
Android 9.0 仿ios的hotseat效果修改hotseat样式
1.概述 在9.0的系统rom定制化的产品中,在launcher3的定制化需求中,有很多功能需求点需要开发,在对一下ui的定制化的过程中,会参考ios的样式进行定制化,所以最近项目需求 要求仿ios的hotseat的样式来进行产品的定制,开发一款仿ios的hotseat,所以需要对hotseat进行分析,然…...
量化私募投资百亿头部量化私募企业在招岗位:AI算法工程师21/22/23届,校招/秋招/社招都看年base60-200万
量化私募投资百亿头部量化私募企业在招岗位:AI算法工程师21/22/23届,校招/秋招/社招都看年base60-200万bonuscut965制度应届需要985本硕博有3年以上相关ai算法经验可放宽学历"岗位职责:base 北京 上海 杭州 深圳1. 利用机器学习、深度学习和人工智能…...
百度西交大大数据菁英班目标检测竞赛
来源:投稿 作者:LSC 编辑:学姐 数据介绍 数据集共包括40000张训练图像和1000张测试图像,每张训练图像对应xml标注文件: 共包含3类:0:head, 1:helmet, 2:person。 提交格式要求,提交名为pred_r…...
Redisson实现分布式锁
目录Redisson简介Redisson实现分布式锁步骤引入依赖application.ymlRedisson 配置类Redisson分布式锁实现Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一,而 Java 是世界上最流行(注意,没有说“最好”)的编程语言之一。…...
【HID基础知识】
蓝牙HID基础知识 一:定义 HID是Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏手柄等。 蓝牙HID 是属于蓝牙协议里面的一个profile, 不管在蓝牙2.0 2.1 3.0还是4.0,5.0的蓝牙中…...
工赋开发者社区 | 工业数字孪生:西门子工业网络与设备虚拟调试案例(TIA+MCD+SINETPLAN)
PART1案例背景及基本情况新生产系统的设计和实施通常是耗时且高成本的过程,完成设计、采购、安装后,在移交生产运行之前还需要一个阶段,即调试阶段。如果在开发过程中的任何地方出现了错误而没有被发现,那么每个开发阶段的错误成本…...
将闲置的Ipad作为Windows的副屏(Twomon SE)
目录一、前言二、方法第一步 安装软件第二步 使用步骤三、注意一、前言 在看网课的时候,总有种不得劲的感觉,来来回回的切换就很糟心~~无意间看见闲置的板砖(Ipad),计上心来-- _ – 期间也尝试过免费的软件ÿ…...
浮点数在内存中的存储——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是浮点数在内存中的存储,昨天我们已经写过了整型在内存中的存储,那么,浮点数在内存中是怎样存储的呢?现在,就让我们进入浮点数在内存中的存储的世界吧…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
