让我百思不得其解的infer究竟是怎么推导类型的?

news2024/12/24 2:55:48

情景再现

有这么一个条件类型的基本语法:

 T extends U ? X : Y; 

如果占位符类型U是一个可以被分解成几个部分的类型,譬如数组类型,元组类型,函数类型,字符串字面量类型等。这时候就可以通过infer来获取U类型中某个部分的类型。

我们再看看下面的这个例子

type InferArray<T> = T extends Array<infer U> ? U : never; 

使用

type I1 = InferArray<[string, number, true]>; // string
type T0 = InferArray<string> // never 

推断结果

我们使用infer关键字声明性地引入了一个名为的新泛型类型变量infer U表示待推断的函数参数 。整句的意思为:如果 T 能 赋值给 Array<infer U>,则结果是Array<infer U> 里的类型U,否则返回never

从上面可以看出,只要是我们传入的时候是数组string[]无论你传入什么类型,它都给你推导出来,如果是只传递了string,这时候推导的它根本不是个数组,条件判断为false,直接返回never

相信通过上面这个粗俗又晦涩例子大家可以明白infer到底能干嘛,以及在什么时候干,大家只需要记住下面的两点:

infer语法的限制如下:

1.infer只能在条件类型的 extends 子句中使用
2.infer得到的类型只能在true语句中使用, 即X中使用

初试牛刀

下面我们来看一道类型体操题目来加深一下infer的用法。

题目:

type TupleA = [number, boolean, string]
type TupleB = [boolean, string, number, unknown?]
type TupleC = [number, number, boolean, boolean] 

问题:

这里有3个元组类型,取出元组类型的第二项,取到的第二项类型是什么?

分析:

虽然看上面的三个元祖类型很少,一眼就看出来里面的第二项是什么类型,但是,如果给你几百个几千个呢?这时候就需要我们使用infer来解决了。

首先

type Second<xxx> = xxx 

第一个xxx参数,泛型这里肯定传的就是不一样的元组类型;第二个xxx就是我们实现的过程,我们要取元组的第二项的类型!

先来写第一个xxx,毕竟比较简单,元组就是个特殊的数组,我们并不知道数组的每一项会是什么类型,所以可以这么写

type Second<Tuple extends unknown[]> = xxx 

第一步完成,这个extends就是限制了我们传入的元组类型,不知道的数组类型(PS:注意我们unknown类型是除了any以外最底层的)。

紧接着第二步,我们就要用到infer了,还要用到extends的另外一种用法,条件判断,具体代码如下:

type Second<Tuple extends unknown[]> = Tuple extends [infer A, infer B, ...infer C] ? B : never 

简单的说,我们这边分别用infer占位了,第一项A,第二项B,然后用展开运算,剩余项用C表示,如果符合了我们这个条件,返回就是B,即第二项,否则就不返回!

题做完了,我们来验证一下答案是否正确:

最终答案:

type TupleA = [number, boolean, string]
type TupleB = [boolean, string, number, unknown?]
type TupleC = [number, number, boolean, boolean]

type Second<Tuple extends unknown[]> = Tuple extends [infer A, infer B, ...infer C] ? B : never

type SecondA = Second<TupleA>
type SecondB = Second<TupleB>
type SecondC = Second<TupleC> 

使用场景

1.推断数组(或者元组)的类型

type InferArray<T> = T extends (infer U)[] ? U : never; 

(infer U)和平时常写的string[]number[]等等是不是很像?这里就是通过(infer U)来获取数组对应的类型。

type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray<string[]>; // string
type I2 = InferArray<number[]>; // number 

2.推断数组(或者元组)第一个元素的类型

type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never 

[infer P, ... infer _]infer P获取的是第一个元素的类型,而...infer _获取的是数组其他剩余元素的数组类型;> 特别说明下,我们例子汇总不需要使用其他元素的类型,所以用_

type I3 = InferFirst<[3, 2, 1]>; // 3 

3.推断数组(或者元组)最后一个元素的类型

type InferLast<T extends unknown[]> = T extends [... infer _, infer Last] ? Last : never; 

这个和推断数组第一个元素的类型类似,...infer _获取的是最后一个元素之前的所有元素类型,infer Last获取的是最后一个元素的类型。

type I4 = InferLast<[3, 2, 1]>; // 1 

4.推断函数类型的参数

type InferParameters<T extends Function> = T extends (...args: infer R) => any ? R : never; 

...args 代表的是函数参数组成的元组, infer R代表的就是推断出来的这个函数参数组成的元组的类型。

type I5 = InferParameters<((arg1: string, arg2: number) => void)>; // [string, number] 

5.推断函数类型的返回值

type InferReturnType<T extends Function> = T extends (...args: any) => infer R ? R : never; 

和前面的推断函数类型的参数类似,=> 后面的infer R代表的就是推断出来的函数的返回值类型。

type I6 = InferReturnType<() => string>; // string 

6.推断Promise成功值的类型

type InferPromise<T> =T extends Promise<infer U> ? U : never; 
type I7 = InferPromise<Promise<string>>; // string 

7.推断字符串字面量类型的第一个字符对应的字面量类型

type InferString<T extends string> = T extends `${infer First}${infer _}` ? First : []; 
type I8 = InferString<"xiumubai">; // J 

出师时刻

接下来我举一些综合性的例子,大家来感受下infer的使用技巧,看看是否能一眼看出来实现的功能,可以按照对应的题目顺序在评论区留言

// Q1
type Shift<T> = T extends [infer L, ...infer R]? [...R] : [];
// A:? 
// Q2
type Pop<T extends any[]> = T extends [...infer L, infer R] ? [...L] : [];
// A:? 
// Q3
type Reverse<T extends unknown[], U extends unknown[] = []> = [] extends T? U: T extends [infer L, ...infer R]? Reverse<R, [L, ...U]>: U;
// A:? 
// Q4
type FlipArguments<T extends Function> = T extends (...arg: infer R) => infer S ? (...arg : Reverse<[...R]>) => S : T; 
: T extends [infer L, ...infer R]? Reverse<R, [L, ...U]>: U;
// A:? 
// Q4
type FlipArguments<T extends Function> = T extends (...arg: infer R) => infer S ? (...arg : Reverse<[...R]>) => S : T; 

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

95后外贸SOHO,年入7位数,他究竟是怎么做的?

外贸SOHO&#xff0c;一年到底能挣多少钱&#xff1f;有人说&#xff1a;“勤勤恳恳&#xff0c;年薪也就十来万吧”&#xff1b;也有人说&#xff1a;“100万而已我早就已经挣到了”&#xff1b;还有人说&#xff1a;“谁说新手难出头&#xff1f;我做跨境半年赚200万&#xf…

Linux设备驱动移植

目录 一、设备树 1.1设备树 1.2设备树文件 1.3设备树语法 1.4Linux内核驱动移植 二、网卡驱动 2.1在make menuconfig界面中选中要安装的驱动 2.2在设备树中添加/修改相应的设备信息 2.3修改时钟 2.4修改eMMc 2.5编译测试 一、设备树 1.1设备树 设备树是一种描述硬…

MATLAB 粒子群算法

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

Cy5 Alkyne,1223357-57-0,花青素Cyanine5炔基,氰基5炔烃

CAS号&#xff1a;1223357-57-0 | 英文名&#xff1a; Cyanine5 alkyne&#xff0c;Cy5 Alkyne | 中文名&#xff1a;花青素CY5炔基CASNumber&#xff1a;1223357-57-0Molecular formula&#xff1a;C35H42ClN3OMolecular weight&#xff1a;556.19Purity&#xff1a;95%Appear…

全网详解MyBatis-Plus updateById方法更新不了空字符串或null的解决方法

文章目录1. 文章引言2. 分析问题3. 解决问题3.1 方法1&#xff1a;全局配置方式3.2 方法2&#xff1a;非null字段验证策略3.3 方法3&#xff1a;通过注解的方式4. 总结1. 文章引言 在开发的过程中&#xff0c;我们经常使用MyBatis-Plus的updateById方法更新数据表&#xff0c;…

ChatGPT学习心得一(使用node+react做了一个案例)

项目地址 http://chat.xutongbao.top 项目截图 使用技术栈 nodeSQLiteredisnginxlog4jsexpressjenkinscdnreactantdreact-scrollbars-customiconfontwebpackpostmanaxiosreduximmutablenpmyarnopenai等等 官网 https://openai.com/blog/chatgpt/ 官方聊天应用 https://ch…

【Linux】多线程编程 - 同步/条件变量/信号量

目录 一.线程同步 1.什么是线程同步 2.为什么需要线程同步 3.如何实现线程同步 二.条件变量 1.常见接口以及使用 2.wiat/signal中的第二个参数mutex的意义 3.代码验证 三.POSIX信号量 1.概念 2.常见接口以及使用 四.条件变量vsPOSIX信号量 一.线程同步 1.什么是线…

公链“私”用

发表时间&#xff1a;2022年3月17日 信息来源&#xff1a;bsvblockchain.org 虽然区块链行业已经诞生了十多年&#xff0c;但直到最近几年这个行业才开始走向专业化并日趋成熟&#xff0c;现在它已成为了一种可为企业实用范例提供合法解决方案的技术。 早年间&#xff0c;与这…

基于ubuntu20.4的wine的MDK5软件的安装

本文基于ubuntu20.4安装MDK5的keil软件&#xff0c;由于MDK不提供linux版本的安装软件&#xff0c;因此需要利用wine软件来安装MDK5软件&#xff0c;具体流程包括wine软件安装、MDK5安装及MDK5的lic添加等3部分内容。具体流程如下所示&#xff1a; &#xff08;一&#xff09;…

typescript 路径别名问题(别名设置 开发与打包时路径问题)

在使用TS开发时&#xff0c;一般我们会在tsconfig 中设置别名来让代码变得优雅一点。 将 import xx from …/…/…/service/改为import xx form ‘service/’ tsconfig 别名设置 此时项目结构如下 一般我们可以通过baseUrl 和path两个字段来设置对应的别名 {"include…

爬虫:栖落的电影网站,利用requests和re模块

这是栖落的电影网站地址&#xff1a;https://xxx.xxx 进入网页&#xff0c;显示&#xff1a; 爬取目标&#xff1a;电影的名称、观影人数和评分。 易知本网站的url url "https://xxx.xxx" 本网站会识别出headers中的python请求而拒绝访问&#xff0c;所以需要更改…

企业管理者不得不看!现在的大企业都是怎么做文档管理的?

最近有一位朋友问我&#xff1a;“如果是大型企业&#xff0c;文档该怎么管理&#xff1f;” 说实话&#xff0c;很多内部的CIO、CTO对这个问题都是束手无策。信息文件散乱、难以和内部的组织构架关联起来、查找困难、不同版本更新进度不一&#xff0c;确实存在一些管理上的难题…

【DSP视频教程】第11期:插补算法,曲线拟合丝滑顺畅,统计函数和基础函数加速实现,汇集SIMD,饱和和MAC乘累加应用实战(2023-02-12)

视频教程汇总帖&#xff1a;https://www.armbbs.cn/forum.php?modviewthread&tid110519 DSP视频教程有段时间没有更新了。 当前DSP库从CMSIS软件包里面独立出来&#xff0c;并且更新非常频繁&#xff0c;所以本期视频教程优先给大家简单介绍下新版DSP&#xff0c; 然后为…

物流运输管理系统源码:实现物流公司全链条管理

一套适用于物流公司的物流运输管理系统&#xff0c;涵盖物流公司内部从订单->提货->运单->配车->点到->预约->签收->回单->代收货款的全链条管理系统。 运行环境&#xff1a;Windows.NET4.0SQLSERVER2008R2 私信了解更多&#xff01; 菜单功能&#…

灰色关联分析法详解及python实践

1. 关于灰色关联分析 1.1. 什么是灰色关联分析 灰色关联分析是指对一个系统发展变化态势的定量描述和比较的方法&#xff0c;其基本思想是通过确定参考数据列和若干个比较数据列的几何形状相似程度来判断其联系是否紧密&#xff0c;它反映了曲线间的关联程度。 在系统发展过…

亚马逊云科技携手滴普科技,打造数据智能新标杆

随着企业数字化转型的不断深入&#xff0c;数据对于业务的价值和重要性也逐渐凸显。越来越多企业意识到&#xff0c;只有不断提升底层数据基础平台的性能和能力&#xff0c;才能构建数据驱动的业务&#xff0c;增强企业核心竞争力。作为湖仓一体数据智能基础软件独角兽企业&…

python基础之变量

Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。 1&#xff1a;什么是变量 变量指的…

纽扣电池上架亚马逊UL4200A检测项目流程

纽扣电池&#xff08;button cell &#xff09;也称扣式电池&#xff0c;是指外形尺寸象一颗小纽扣的电池&#xff0c;一般来说直径较大&#xff0c;厚度较薄。纽扣电池因体形较小&#xff0c;故在各种微型电子产品中得到了广泛的应用&#xff0c;直径从4.8mm至30mm&#xff0c…

腾讯云轻量应用服务器和云服务器CVM区别

腾讯云轻量级服务器和云服务器有什么区别&#xff1f;轻量级应用服务器和云服务器CVM哪个更好&#xff1f;无论成本和使用门槛如何&#xff0c;云服务器CVM都更好&#xff1b;从性价比和易用性的角度来看&#xff0c;轻量级应用服务器的成本更低。来详细谈谈腾讯云轻量级应用服…

LabVIEW项目或库文件已损坏

LabVIEW项目或库文件已损坏尝试打开项目文件&#xff08;*.lvproj&#xff09;时&#xff0c;不断收到错误&#xff1a;项目或库文件已损坏。当尝试在新计算机上打开类库时&#xff0c;看到错误内存或数据结构损坏。无法加载文件&#xff0c;但库在另一台计算机上打开正常。如何…