让我百思不得其解的infer究竟是怎么推导类型的?
情景再现
有这么一个条件类型的基本语法:
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 : nevertype 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个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享
相关文章:

让我百思不得其解的infer究竟是怎么推导类型的?
情景再现 有这么一个条件类型的基本语法: T extends U ? X : Y; 如果占位符类型U是一个可以被分解成几个部分的类型,譬如数组类型,元组类型,函数类型,字符串字面量类型等。这时候就可以通过infer来获取U类型中某个部分的类型。 …...
E8-怎么实现根据表单内容自动生成标题
背景 可能有些小伙伴看到这个话题,觉得非常简单,在发起人步骤设置标题可编辑,在有关的控件中设置事件去更新标题就可以了呗。但如果想要标题字段里包含编号呢,而编号是在具体路径的高级设置里设置的,在某个出口生成的…...

《c语言深度解剖》--一套非常经典的笔试题
学习完c语言,需要对所学知识进行一个检测,下面有一套笔试题, 你有四十分钟进行检测,每道题五分,严格要求自己打分。 根据作者原话:在没有何提示的情况下,如果能得满分,那你可以扔掉本书了,因为你的水平已经…...

【数据结构与算法】单调队列 | 单调栈
🌠作者:阿亮joy. 🎆专栏:《数据结构与算法要啸着学》 🎇座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉…...
openh264解码h264视频帧主流程
一 解析一帧的入口int32_t WelsDecodeSlice (PWelsDecoderContext pCtx, bool bFirstSliceInLayer, PNalUnit pNalCur) {// 解码slicePDqLayer pCurDqLayer pCtx->pCurDqLayer;PFmo pFmo pCtx->pFmo;int32_t iRet;int32_t iNextMbXyIndex, iSliceIdc;PSlice pSlice &a…...
【个人笔记】C语言位域
一句话解释位域:指定结构体内变量的的位宽,从而节省空间 例子: #include <stdio.h> struct _test {int bit1:3; // 第一个字节0 ~ 2位 int :0; // 空域:表示第一个字节 3~7都为0int bit2:1; // 第二个字节第0位int :3;…...
ROS笔记(1)——ROS的核心概念
目录 节点与管理器 话题通信 服务通信 参数——全局共享字典 节点与管理器 节点——执行单元 (1)执行具体任务的进程、独立运行的可执行文件 (2)不同节点可使用不同的编程语言,可分布式运行在不同的主机 ÿ…...

动态SQL使用【JavaEE】
动态SQL使用 1. if 标签 判断一个参数是否有值,如果没值,那么就会隐藏 if 中的 sql 语法: <if test"username!null">username#{username} </if>表达式:username 的参数是否为空 如果结果为 true,…...
leetcode刷题
1、stack栈相关 top():返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。 push(const T& obj):可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。 push(T&& obj)࿱…...

移动设备安全管理基础指南
什么是移动安全管理 (MSM) 移动安全管理是指为保护企业中的移动设备和企业数据而采取的行动。这些操作可以进一步被归类为反应性的或主动的,基于该操作是在数据和设备被破坏之前还是之后执行的。除了管理移动设备外,大多数MDM解决…...

【Java|多线程与高并发】 使用Thread 类创建线程的5种方法如何查看程序中的线程
文章目录前言线程创建1.继承Thread类重写run()方法如何查看程序中的线程?2.实现Runnable接口3.使用匿名内部类,继承Thread4.使用匿名内部类,实现Runnable5.⭐使用Lambda表达式,创建线程(重要)Thread 的常见构造方法总结前言 在这里主要补充说明一些问题,方便更好地理解下面的…...

零基础学MySQL(五)-- 详细讲解数据库中的常用函数
目录🎇一、聚合函数1️⃣count 函数(1)基本语法(2)基本练习(3)注意细节2️⃣sum 函数(1)基本语法(2)基本练习(3)注意细节3…...

第4章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环...
第 4 章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环 4.1 分支控制 if-else 让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支 4.1.1 单分支 1)基本语法…...

2.4G-WiFi连接路由器过程
一、概述 WiFi的数据通信基于802.11协议进行,无线AP在工作时会定时向空中发送beacon数据包,基站(STA)从beacon中解析出AP的名称、加密方式等信息,从而发起连接。 二、WiFi连接路由器的详细过程 WiFi连接过程主要可以…...

3. SpringMVC Rest 风格
1. REST 简介 REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格。 当要表示一个网络资源的时候,可以使用两种方式: 传统风格资源描述形式 http://localhost/user/getById?…...
Python3简介
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。 Python 是面向对象语言: 这意味着P…...

如何学习PMP?
★基础要打牢 方法:“基础不牢,地动山摇”,如果基础不牢那么就很难拿高分,因为连最基础的题目分都不一定能拿到。 可以在针对基础知识,把PMBOK看一两遍,再次加深印象,再把平时做章节练习、每日5…...

【DSP视频教程】第11期:插补算法,曲线拟合丝滑顺畅,统计函数和基础函数加速实现,汇集SIMD,饱和和MAC乘累加应用实战(2023-02-12)
视频教程汇总帖:https://www.armbbs.cn/forum.php?modviewthread&tid110519 DSP视频教程有段时间没有更新了。 当前DSP库从CMSIS软件包里面独立出来,并且更新非常频繁,所以本期视频教程优先给大家简单介绍下新版DSP, 然后为…...

分类模型评估:混淆矩阵、准确率、召回率、ROC
1. 混淆矩阵 在二分类问题中,混淆矩阵被用来度量模型的准确率。因为在二分类问题中单一样本的预测结果只有Yes or No,即:真或者假两种结果,所以全体样本的经二分类模型处理后,处理结果不外乎四种情况,每种…...

算法 ——世界 一
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。个人爱好: 编程,打篮球,计算机知识个人名言:海不辞水,故能成其大;山不辞石…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...