当前位置: 首页 > news >正文

让我百思不得其解的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是一个可以被分解成几个部分的类型&#xff0c;譬如数组类型&#xff0c;元组类型&#xff0c;函数类型&#xff0c;字符串字面量类型等。这时候就可以通过infer来获取U类型中某个部分的类型。 …...

E8-怎么实现根据表单内容自动生成标题

背景 可能有些小伙伴看到这个话题&#xff0c;觉得非常简单&#xff0c;在发起人步骤设置标题可编辑&#xff0c;在有关的控件中设置事件去更新标题就可以了呗。但如果想要标题字段里包含编号呢&#xff0c;而编号是在具体路径的高级设置里设置的&#xff0c;在某个出口生成的…...

《c语言深度解剖》--一套非常经典的笔试题

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

【数据结构与算法】单调队列 | 单调栈

&#x1f320;作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《数据结构与算法要啸着学》 &#x1f387;座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;…...

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语言位域

一句话解释位域&#xff1a;指定结构体内变量的的位宽&#xff0c;从而节省空间 例子&#xff1a; #include <stdio.h> struct _test {int bit1:3; // 第一个字节0 ~ 2位 int :0; // 空域&#xff1a;表示第一个字节 3~7都为0int bit2:1; // 第二个字节第0位int :3;…...

ROS笔记(1)——ROS的核心概念

目录 节点与管理器 话题通信 服务通信 参数——全局共享字典 节点与管理器 节点——执行单元 &#xff08;1&#xff09;执行具体任务的进程、独立运行的可执行文件 &#xff08;2&#xff09;不同节点可使用不同的编程语言&#xff0c;可分布式运行在不同的主机 &#xff…...

动态SQL使用【JavaEE】

动态SQL使用 1. if 标签 判断一个参数是否有值&#xff0c;如果没值&#xff0c;那么就会隐藏 if 中的 sql 语法&#xff1a; <if test"username!null">username#{username} </if>表达式&#xff1a;username 的参数是否为空 如果结果为 true&#xff0c…...

leetcode刷题

1、stack栈相关 top()&#xff1a;返回一个栈顶元素的引用&#xff0c;类型为 T&。如果栈为空&#xff0c;返回值未定义。 push(const T& obj)&#xff1a;可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。 push(T&& obj)&#xff1…...

移动设备安全管理基础指南

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

【Java|多线程与高并发】 使用Thread 类创建线程的5种方法如何查看程序中的线程

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

零基础学MySQL(五)-- 详细讲解数据库中的常用函数

目录&#x1f387;一、聚合函数1️⃣count 函数&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;基本练习&#xff08;3&#xff09;注意细节2️⃣sum 函数&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;基本练习&#xff08;3&#xff09;注意细节3…...

第4章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环...

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

2.4G-WiFi连接路由器过程

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

3. SpringMVC Rest 风格

1. REST 简介 REST&#xff08;Representational State Transfer&#xff09;&#xff0c;表现形式状态转换&#xff0c;它是一种软件架构风格。 当要表示一个网络资源的时候&#xff0c;可以使用两种方式&#xff1a; 传统风格资源描述形式 http://localhost/user/getById?…...

Python3简介

Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&#xff0c;相比其他语言经常使用英文关键字&#xff0c;其他语言的一些标点符号&#xff0c;它具有比其他语言更有特色语法结构。 Python 是面向对象语言: 这意味着P…...

如何学习PMP?

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

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

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

分类模型评估:混淆矩阵、准确率、召回率、ROC

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

算法 ——世界 一

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解

一、前言 在HarmonyOS 5的应用开发模型中&#xff0c;featureAbility是旧版FA模型&#xff08;Feature Ability&#xff09;的用法&#xff0c;Stage模型已采用全新的应用架构&#xff0c;推荐使用组件化的上下文获取方式&#xff0c;而非依赖featureAbility。 FA大概是API7之…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...

虚幻基础:角色旋转

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 移动组件使用控制器所需旋转&#xff1a;组件 使用 控制器旋转将旋转朝向运动&#xff1a;组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转&#xff1a;必须移动才能旋转&#xff0c;不移动不旋转控制器…...

Cursor AI 账号纯净度维护与高效注册指南

Cursor AI 账号纯净度维护与高效注册指南&#xff1a;解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后&#xff0c;许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...