真实案例说明 TypeScript 类型体操的意义
TypeScript 类型系统支持类型编程,真实也就是案例对类型参数做一系列运算产生新的类型。比如这样:
type isTwo<T> = T extends 2 ?说明 true: false;这种类型编程逻辑可以写的很复杂,所以被戏称为“类型体操”。类型
它是体操 TS 中最强大也是最复杂的部分了,属于深水区的真实内容。
很多同学不知道类型编程学了有什么用,案例好像做业务也用不到这个。说明那今天我们就来看一个具体的类型例子,来感受下类型体操的体操意义。
我们想实现这样一个 JS 方法:
function parseQueryString(queryStr) {
if (!queryStr || !queryStr.length) {
return {};
}
const queryObj = {};
const items = queryStr.split(&);
items.forEach(item => {
const [key,真实 value] = item.split(=);
if (queryObj[key]) {
if(Array.isArray(queryObj[key])) {
queryObj[key].push(value);
} else {
queryObj[key] = [queryObj[key], value]
}
} else {
queryObj[key] = value;
}
});
return queryObj;
}这段代码很容易看出来就是做 query string 的 parse 的,会把 a=1&b=2&c=3 的案例字符串 parse 成 { a: 1, b: 2, c: 3 } 返回。如果有同名的说明 key 的话,就合并到一个数组里。类型
JS 的体操逻辑大家写的比较多,这部分很容易理解:

那如果要给这个函数加上类型,大家会怎么加呢?
我猜,亿华云计算大部分人会这么加:

参数是 string 类型,返回值是 parse 之后的对象类型 object。
这样是可以的,而且 object 还可以写成 Record,因为对象是索引类型(索引类型就是聚合多个元素的类型,比如对象、class、数组都是)。

Record 是 TS 内置的一个高级类型,是通过映射类型的语法来生成索引类型的:
type Record = { [P in K]: T;}比如传入 a | b 作为 key,1 作为 value,就可以生成这样索引类型:

所以这里的 Record 也就是 key 为 string 类型,value 为任意类型的索引类型,可以代替 object 来用,更加语义化一点:

但是不管是企商汇返回值类型为 object 还是 Record 都存在一个问题:返回的对象不能提示出有哪些属性:

对于习惯了 ts 的提示的同学来说,没有提示太不爽了。怎么能让这个函数的返回的类型有提示呢?
这就要用到类型编程了。
我们把函数的类型定义改成这样:

声明一个类型参数 Str,约束为 string 类型,函数参数的类型指定是这个 Str,返回值的类型通过对 Str 做类型运算得到,也就是 ParseQueryString。
这个 ParseQueryString 的类型做的事情就是把传入的 Str 通过各种类型运算产生对应的索引类型。
这样返回的类型就有提示了:

是不是很神奇!这就是类型体操的魅力!能够实现更精准的类型提示。
那这个 ParseQueryString 的WordPress模板高级类型是怎么实现的呢?
其实我们实现过:TS 类型体操:图解一个复杂高级类型,这里再讲一下:
首先我们要把 a=1&b=2&c=3 的字符串按照 & 分割开,使用模式匹配的方式。
把提取出来的每一个 a=1、b=2、c=3 这种字符串再做一次处理,把结果合并起来返回。
也就是:
type ParseQueryString
= Str extends `${infer Param}&${infer Rest}`
? MergeParams
对提取出来的 Param 再做处理,也就是 ParseParam,剩下的递归处理,也就是 ParseQueryString,然后把结果合并。
如果模式匹配不满足,就说明没有 & 了,那就把剩下的也做一次处理返回。
这里的 ParseParam 就是处理 a=1、b=2、c=3 这种字符串的,也是通过模式匹配来提取:
type ParseParam =
Param extends `${infer Key}=${infer Value}`
? {
[K in Key]: Value
} : Record<string, any>;类型参数 Param 是待处理的字符串,通过模式匹配提取 = 分隔的字符串到局部变量 Key 和 Value 中,构造成索引类型返回;
如果模式匹配不满足,说明不是 = 分隔的字符串字面量类型,就返回 Record 代表任意索引类型。

测试下:

然后对多个索引类型的合并,就是通过映射类型的语法构造一个新的索引类型:
type MergeParams<
OneParam extends Record
OtherParam extends Record
> = {
readonly [Key in keyof OneParam | keyof OtherParam]:
Key extends keyof OneParam
? Key extends keyof OtherParam
? MergeValues
: OneParam[Key]
: Key extends keyof OtherParam
? OtherParam[Key]
: never
}类型参数 OneParam 和 OtherParam 是两个索引类型,通过 Record 来约束。
通过映射类型的语法构造一个新的索引类型返回,Key 来自两者的合并,也就是 Key in keyof OneParam | keyof OtherParam。并且加上一个 readonly 的修饰,这样就是让返回的索引类型不能被修改。
值要判断下如果是两者都有的值,那就做合并,否则分别取对应的值。
合并的逻辑是这样的:
type MergeValues
One extends Other
? One
: Other extends unknown[]
? [One, ...Other]
: [One, Other];类型参数 One、Other 为待合并的两个值的类型,如果两个一样就返回其中一个,否则如果是数组就合并数组,也就是 [One, ...Other],否则把两个值合并成数组 [One, Other]。
这样就完成了两个索引类型的合并,测试下:

整体测试下:

成功了!我们实现了 ParseQueryString 的高级类型!(如果对类型体操看不明白的话,可以去看下我的小册《TypeScript 类型体操通关秘籍》补下基础)。
当然,这只是纯粹的类型体操,把它用到 JS 里才是最终目的,所以我们把 parseQueryString 的类型定义改成了这样:

把函数参数的类型传入 ParseQueryString 的高级类型做类型运算,返回的结果作为函数返回值的类型。(这里要用 as any 把返回值断言为 any,因为默认推导的类型不精确,我们用根据 Str 动态算出来的类型)
这样也就能实现精准的类型提示:

并且因为我们 readonly 的限制,不能修改属性的值:

对比下没用类型体操的时候:

就可以得出结论:
类型编程可以通过类型运算产生更准确的类型,配合编辑器可以做更精准的类型提示和检查,这就是类型体操的意义。
总结类型编程是 TypeScript 的深水区内容,它是对类型做一系列类型运算后产生新的类型,它可以实现更精准的类型提示和检查。
我们通过 parseQueryString 这个函数的类型定义来直观感受了下用类型体操和不用类型体操的区别,在类型提示这方面,体验是相差很多的。
实现更精准的类型提示和检查,这就是类型体操的意义!
相关文章
电脑开机显示停止错误屏幕的解决方法(解决电脑开机时出现停止错误屏幕的常见故障)
摘要:在使用电脑的过程中,有时候我们会遇到电脑在开机时显示停止错误屏幕的情况,这时候我们需要采取一些措施来解决这个问题,以确保电脑能够正常启动并运行。本文将介绍一些常见的故障原因以及相应...2025-11-05Oppo手机功能强大的优势(探索Oppo手机功能,领略智能科技魅力)
摘要:随着智能手机的不断发展,Oppo作为一家知名的手机制造商,以其出色的功能而脱颖而出。本文将深入探索Oppo手机的强大功能,为读者展示其创新科技和智能魅力。一:美学设计——外...2025-11-05卧龙大数据(以卧龙大数据为例,探索大数据应用的发展趋势与前景)
摘要:近年来,随着大数据技术的迅猛发展,大数据应用正逐渐渗透到各个领域。卧龙大数据作为一家领先的大数据解决方案提供商,不仅在技术创新方面取得了重要突破,而且在实际应用中取得了显著成效。本...2025-11-05丽台Quadro4000(性能卓越,应用广泛的创意设计加速器)
摘要:在当今数字化时代,创意设计行业对于高性能显卡的需求日益增长。丽台Quadro4000作为一款专为专业创作者打造的显卡,以其卓越的性能和广泛的应用领域成为行业的领先选择。本文将从不同...2025-11-05安卓手机Root权限开启技巧(掌握Root权限,解锁无限可能)
摘要:在安卓手机的世界中,拥有Root权限意味着你可以完全掌控你的设备,解锁更多的功能和定制选项。然而,对于很多普通用户来说,如何开启Root权限可能是一项复杂和困惑的任务。本文将介绍一...2025-11-05电脑光盘安装教程(一步一步教你如何使用电脑光盘安装软件,省去繁琐的下载步骤)
摘要:在电脑使用过程中,我们经常需要安装各种软件来满足不同的需求。尽管如今互联网发达,可以通过下载来获取软件,但有些时候我们仍然需要使用光盘进行安装。本文将详细介绍如何使用电脑光盘进行软...2025-11-05

最新评论