热门标签 | 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


推荐阅读
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文介绍了如何使用JQuery实现省市二级联动和表单验证。首先,通过change事件监听用户选择的省份,并动态加载对应的城市列表。其次,详细讲解了使用Validation插件进行表单验证的方法,包括内置规则、自定义规则及实时验证功能。 ... [详细]
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • Yii 实现阿里云短信发送 ... [详细]
  • 深入理解C++中的KMP算法:高效字符串匹配的利器
    本文详细介绍C++中实现KMP算法的方法,探讨其在字符串匹配问题上的优势。通过对比暴力匹配(BF)算法,展示KMP算法如何利用前缀表优化匹配过程,显著提升效率。 ... [详细]
  • 将Web服务部署到Tomcat
    本文介绍了如何在JDeveloper 12c中创建一个Java项目,并将其打包为Web服务,然后部署到Tomcat服务器。内容涵盖从项目创建、编写Web服务代码、配置相关XML文件到最终的本地部署和验证。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
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社区 版权所有