热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TS类型体操:图解一个复杂高级类型

ts,类型,体操,图解,一

今天就来做个高难度的体操,它会综合运用模式匹配、构造、递归等套路,对提升类型编程水平很有帮助。

我们要实现的高级类型如下:

它的类型参数是参数字符串 query string,会返回解析出的参数对象,如果有同名的参数,会把值做合并。

先不着急实现,我们先回顾下相关的类型体操基础:

类型体操基础

模式匹配

模式匹配是指用一个类型匹配一个模式类型来提取其中的部分类型到 infer 声明的局部变量中。

比如提取 a=b 中的 a 和 b:

这种模式匹配的套路在数组、字符串、函数等类型中都有很多应用。

构造

映射类型用于生成索引类型,生成的过程中可以对索引或者索引值做一些修改。

比如指定 key 和 value 来生成一个索引类型:

递归

TypeScript 高级类型支持递归,可以处理数量不确定的问题。

比如不确定长度的字符串的反转:

type ReverseStrextends string, Result extends string = '' > = Str extends `${infer First}${infer Rest}` ? ReverseStr`${First}${Result}`> : Result; 

简单了解下模式匹配、构造、递归都是什么之后,就可以开始实现这个复杂的高级类型 ParseQueryString 了:

思路分析

假设有这样一个 query string: a=1&a=2&b=3&c=4

我们要首先把它分成 4 部分:也就是 a=1、a=2、b=3、c=4。这个就是用通过上面讲的模式匹配来提取。

每一部分又可以进一步处理,提取出 key value 构造成索引类型,比如 a=1 就可以通过模式匹配提取出 a、1,然后构造成索引类型 {a: 1}。

这样就有了 4 个索引类型 {a:1}、{a:2}、{b:3}、{c:4}。

结下来把它合并成一个就可以了,合并的时候如果有相同的 key 的值,要放到数组里。

就产生了最终的索引类型:{a: [1,2], b: 3, c: 4}

整体流程是这样的:

其中第一步并不知道有多少个 a=1、b=2 这种 query param,所以要递归的做模式匹配来提取。

这就是这个高级类型的实现思路。

下面我们具体来写一下:

代码实现

我们按照上图的顺序来实现,首先提取 query string 中的每一个 query param:

query param 数量不确定,所以要用递归:

type ParseQueryStringextends string> = Str extends `${infer Param}&${infer Rest}` ? MergeParams, ParseQueryString> : ParseParam; 

类型参数 Str 为待处理的 query string。

通过模式匹配提取其中第一个 query param 到 infer 声明的局部变量 Param 中,剩余的字符串放到 Rest 中。

用 ParseParam 来处理 Param,剩余的递归处理,最后把它们合并到一起,也就是 MergeParams, ParseQueryString> 。

如果模式匹配不满足,说明还剩下最后一个 query param 了,也用 ParseParam 处理。

然后分别实现每一个 query param 的 parse:

这个就是用模式匹配提取 key 和 value,然后构造一个索引类型:

type ParseParamextends string> = Param extends `${infer Key}=${infer Value}` ? { [K in Key]: Value } : {}; 

这里构造索引类型用的就是映射类型的语法。

先来测试下这个 ParseParam:

做完每一个 query param 的解析了,之后把它们合并到一起就行:

合并的部分就是 MergeParams:

type MergeParamsextends object, OtherParam extends object > = { [Key in keyof OneParam | keyof OtherParam]: Key extends keyof OneParam ? Key extends keyof OtherParam ? MergeValues : OneParam[Key] : Key extends keyof OtherParam ? OtherParam[Key] : never } 

两个索引类型的合并也是要用映射类型的语法构造一个新的索引类型。

key 是取自两者也就是 key in keyof OneParam | keyof OtherParam。

value 要分两种情况:

  • 如果两个索引类型都有的 key,就要做合并,也就是 MergeValues。
  • 如果只有其中一个索引类型有,那就取它的值,也就是 OtherParam[key] 或者 OneParam[Key]。

合并的时候,如果两者一样就返回任意一个,如果不一样,就合并到数组里返回,也就是 [One, Other]。如果本来是数组的话,那就是数组的合并 [One, ...Other]。

type MergeValues = One extends Other ? One : Other extends unknown[] ? [One, ...Other] : [One, Other]; 

测试下 MergeValues:

这样,我们就实现了整个高级类型,整体测试下:

这个案例综合运用到了递归、模式匹配、构造的套路,还是比较复杂的。

可以对照着这张图来看下完整代码:

type ParseParamextends string> = Param extends `${infer Key}=${infer Value}` ? { [K in Key]: Value } : {}; type MergeValues = One extends Other ? One : Other extends unknown[] ? [One, ...Other] : [One, Other]; type MergeParamsextends object, OtherParam extends object > = { [Key in keyof OneParam | keyof OtherParam]: Key extends keyof OneParam ? Key extends keyof OtherParam ? MergeValues : OneParam[Key] : Key extends keyof OtherParam ? OtherParam[Key] : never } type ParseQueryStringextends string> = Str extends `${infer Param}&${infer Rest}` ? MergeParams, ParseQueryString> : ParseParam; type ParseQueryStringResult = ParseQueryString<'a=1&a=2&b=2&c=3'>; 

总结

我们首先复习了下 3 种类型体操的套路:

  • 模式匹配:一个类型匹配一个模式类型,提取其中的部分类型到 infer 声明的局部变量中
  • 构造:通过映射类型的语法来构造新的索引类型,构造过程中可以对索引和值做一些修改
  • 递归:当处理数量不确定的类型时,可以每次只处理一个,剩下的递归来做

然后用这些套路来实现了一个 ParseQueryString 的复杂高级类型。

如果能独立实现这个高级类型,说明你对这三种类型体操的套路掌握的就挺不错的了。

最后

如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !

PHP学习手册:https://doc.crmeb.com
技术交流论坛:https://q.crmeb.com


推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文详细介绍了PHP中与URL处理相关的三个函数:http_build_query、parse_str和查询字符串的解析。通过示例和语法说明,讲解了这些函数的使用方法和作用,帮助读者更好地理解和应用。 ... [详细]
  • VSCode快速查看函数定义和代码追踪方法详解
    本文详细介绍了在VSCode中快速查看函数定义和代码追踪的方法,包括跳转到定义位置的三种方式和返回跳转前的位置的快捷键。同时,还介绍了代码追踪插件的使用以及对符号跳转的不足之处。文章指出,直接跳转到定义和实现的位置对于程序员来说非常重要,但需要语言本身的支持。以TypeScript为例,按下F12即可跳转到函数的定义处。 ... [详细]
  • 本文介绍了JavaScript进化到TypeScript的历史和背景,解释了TypeScript相对于JavaScript的优势和特点。作者分享了自己对TypeScript的观察和认识,并提到了在项目开发中使用TypeScript的好处。最后,作者表示对TypeScript进行尝试和探索的态度。 ... [详细]
  • Problemexplanation: ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • 花瓣|目标值_Compose 动画边学边做夏日彩虹
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Compose动画边学边做-夏日彩虹相关的知识,希望对你有一定的参考价值。引言Comp ... [详细]
  • Thisworkcameoutofthediscussioninhttps://github.com/typesafehub/config/issues/272 ... [详细]
  • 初始化初始化本地空版本库,仓库,英文名repositorymkdirtest&&cdtestgitinit克隆项目到本地gitclone远程同 ... [详细]
  • 欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个图,问是否存在欧拉回路?Input测 ... [详细]
  • 湍流|低频_youcans 的 OpenCV 例程 200 篇106. 退化图像的逆滤波
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了youcans的OpenCV例程200篇106.退化图像的逆滤波相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
勇气的爱_961
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有