大家好,我是苏先生,一名热爱钻研、乐于分享的前端工程师,跟大家分享一句我很喜欢的话:人活着,其实就是一种心态,你若觉得快乐,幸福便无处不在
前言
前边三篇文章我们一共实现了25个工具类型,按照本专栏的规划,还差74个...
本节我们继续学习元组相关的类型编程
image.png提示
对于语法层面的知识点本系列(类型体操开头的标题)不会展开说明哈,可以自行搜索学习其他大佬的优质文章或者等我后续更新补充
使用示例
- 示例一
按照参数二逐一进行替换
type Result = Fill<[1, 2, 3], 'a'> // ['a', 'a', 'a']
- 示例二
按照参数3替换指定位置
type Result = Fill<[1, 2, 3], 'a',2> // [1, 'a', 3]
- 示例三
将区间[2,4)的元素进行替换
type Result = Fill<[1, 2, 3, 4], 'a',2,4> // [1, 'a', 'a', 4]
实现
目前为止,我们实现的都是一些简单的工具类型,为了不让大家小看TypeScript,我这次稍微提升了下难度。现在我们暂停花两分钟,结合使用示例先自己思考一下
image.png版本一
首先我们要确认下泛型参数的个数,它至少应该有T、U两个参数:T表示数组本身、U表示要替换填充的值
type Fill<T,U>
接着我们来考虑泛型参数的限制条件,显然,T必须是数组类型,U则可以是any类型不需要约束
type Fill<T extends any[],U>
在只有两个参数的情况下,该版本功能上实际就是拿U去一个一个替换掉T中的元素,至于每一个元素的类型是啥,其实都无所谓,所以我们先把原数据进行拆分,拿到数组中的第一个元素
[F,...R]
我们只需要将F替换成U,就完成了在索引为0位置的替换
[U,...R]
下一次,我们只需要继续对L进行拆分成F和R,并使用U替换F即可
type Fill<T extends any[],U> = T extends [infer F,...infer R] ? [U,...Fill<R,U>] : T
使用示例
image.png但是目前我们的F类型是多余的,它被显示为暗淡色,尽管它不影响,但是看着别扭,所以我们使用T[0]来代替
image.png版本二
现在我们来考虑参数三,它代表索引位置,因此我们使用I来表示
type Fill<T extends any[],U,I>
同时我们对其进行约束,使其必须是number类型,并且它应是可选的
type Fill<T extends any[],U,I extends number = 0>
在版本一中,我们相当于每次固定的去替换了索引为0的位置,由于I是一个动态的值,因此我们不得不进行判断,以确定什么时候应该应用U,关于判断本身也不难
2 extends 3 ? true : false
在TypeScript中跟位置相关的有number和length,但前者是用来获取元素的,故排除掉,就只剩下length了,但这表示的是数组的长度,乍一看似乎跟I关联不上...
不过再仔细想想,我们在递归过程中,不就是每次都少一位吗?
image.pngT['length'] extends I ? '匹配到索引位置I' : '未匹配到'
现在我们来分析两个分支:
- 匹配到时
由于的T['length']是在不断减少的,所以我们应该将最后一位替换成U,剩下的接着丢去递归
[...Fill<F,U,I>,U]
- 不匹配时
我们不需要做任何处理,将一开始拆出来的,再原封不动的拼接回去就好了
[...Fill<F,U,I>,L]
综上所述,最终实现如下
type Fill<
T extends unknown[],
U,
I extends number = 0,
> = T extends [...infer F,infer L]
? T['length'] extends I
? [...Fill<F,U,I>,U]
: [...Fill<F,U,I>,L]
: T
使用示例
image.png版本三
版本二的实现虽然满足了示例二,但会导致示例一失败,因此,我们还需要对其进行下兼容
image.png我们观察版本二与版本一的区别,主要体现在拼回时是取原值L还是U进行替换,而这又恰好与I的取值有关,当I为0时,说明取得是默认值,此时我们切换为取U就好了
type Fill<
T extends unknown[],
U,
I extends number = 0,
> = T extends [...infer F,infer L]
? T['length'] extends I
? [...Fill<F,U,I>,U]
: [...Fill<F,U,I>, I extends 0 ? U : L]
: T
使用示例
image.png版本四
现在,我们来考虑参数四,它将与参数三组成一个前闭后开的范围,为了更好的语意,我们将I修改为S,表示start开始位置
type Fill<
T extends unknown[],
U,
S extends number = 0,
E
>
E的类型和S相同,不过默认值要有差别,它应该是T['length']
type Fill<
T extends unknown[],
U,
S extends number = 0,
E extends number = T['length'],
>
比较容易想到的是,它应该也必须和I一样,通过构建条件类型来进行判断,因此,我们必须构建一个类型来对T['length']去反
为此,我们需要声明一个类型变量用于记录当前已经处理了几个元素,这里我们使用C来表示count计数
type Fill<
T extends unknown[],
U,
S extends number = 0,
E extends number = T['length'],
C extends any[] = []
>
接着在递归调用处不断的给C添加新的元素,此时,每一轮的C['length'] + T['length']都刚好等于初始时的T['length']
Fill<F,U,I,[...C,any]>
现在我们来为递归找出口,即当C['length']与E相等时,后续的元素都不需要再进行处理了
C['length'] extends E ? T : '待处理'
现在,我们接着思考:如果当遇到S时我们能够有一个变量来标记开始,然后在遇到E前,都强制取U,是不是就正好是我们想要的功能了?为此,我们需要声明一个类型变量M来进行标记,并且它默认与S进行比较
type Fill<
...
M extends boolean = C['length'] extends S ? true : false
>
此时,当遇到S时,我们标记为true,后续的递归全部沿用此标记,强制取U就可以了。另外,由于我们已经将出口前置,所以是不需要做将M切回false的处理的
故实现代码如下
type Fill<
T extends unknown[],
U,
S extends number = 0,
E extends number = T['length'],
C extends any[] = [U],
M extends boolean = C['length'] extends S ? true : false
> = C['length'] extends E
?T
:T extends [infer F , ... infer R]
?M extends true
?[U,...Fill<R,U,S,E,[...C,any],M>]
:[F,...Fill<R,U,S,E,[...C,any]>]
:T
使用示例
image.png版本五
对于版本四,它对示例三正按照预期进行工作,但是其无法兼容示例一和示例二,这其实也不难实现,我们只需要将示例二作为单独的工具类型,并在符合条件时进行调用就可以啦
type Fill<
T extends unknown[],
U,
S extends number = 0,
E extends number = T['length'],
C extends any[] = S extends 0 ? [] : E extends T['length'] ? [] : [U],
M extends boolean = C['length'] extends S ? true : false,
L extends boolean = C extends [] ? true : false
> = C['length'] extends E
?T
:T extends [... infer R,infer F ]
? L extends true
? Comppat<T,U,S>
:M extends true
?[...Fill<R,U,S,E,[...C,any],M>,U]
:[...Fill<R,U,S,E,[...C,any]>,F]
:T
type Comppat<
T extends unknown[],
U,
I extends number = 0,
> = T extends [...infer F,infer L]
? T['length'] extends I
? [...Comppat<F,U,I>,U]
: [...Comppat<F,U,I>, I extends 0 ? U : L]
: T
下期预告
Reverse
- 功能
将数组类型中的元素进行翻转
- 使用示例
type Res = Reverse<[1,2]> // [2,1]
Filter
- 功能
从数组类型中挑选指定的类型并返回一个由挑选类型组成的新数组
- 使用示例
type Res = Filter<[1,2],2> // [2]
如果本文对您有用,希望能得到您的点赞和收藏
订阅专栏,每周更新2-3篇类型体操,每月1-3篇vue3源码解析,等你哟😎
网友评论