持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
前言
今天碰到一个比较有意思的问题,如何有TS去定义一个正整数数组。
我们都知道 TS 定义数字数组的方法很简单:
type NumberArr = number[]
那么现在问题来了,要怎么在不支持数组加减的TS中,定义一个正整数数组呢?
问题拆分
首先我们可以按照 number[] 的方式,那么前面的这个 number 就应该换成正整数,那么我们首先需要解决的问题就是,应该如何去定义一个正整数?
定义正整数
正整数,可以看成是 0,1,2,3... 一直到无穷大的数的集合,那么放在 TS 中就是 1|2|3|4... 这样的一个联合类型,就能够支持所有的正整数。
那么我们这个问题就变成了,应该要如何定义一个由正整数组成的联合类型 - 1|2|3|4...
首先通过手打的这种方式肯定是不可取的,先不说要是成千上百的范围要写多少冗余代码,作为程序员,就不能容忍这种定义方式。
所以我们的解决思路就出来了,定义一个工具类型,能够根据输入的数字,返回 0 到这个数字的联合类型。
GetIntegerUnion 工具类型
在 TS 中我们没有办法通过操作字面量类型来获得不同而字面量,因为对于TS来说只有字面量,没有数字这一概念。
但是可以通过数组的 'length' 来获取到不同的数字,并且可以通过操作数组的长度来获取到不同的数字
那么思路就来了,我们有没有办法写一个工具类型,给它一个数字,然后内部会去创建这个数字长度的数组,并且 在创建的过程中,就可以不断地返回数组的长度,用他们组成一个联合类型
再此之前我们先来考虑一下,给你一个数字,要如何生成这个数字长度的数组呢?
type NewArr<N extends number,Arr extends any[] = []>
= Arr['length'] extends N
? Arr
: NewArr<N,[any,...Arr]>
答案是通过递归的方式,我们能够通过入参来创建一个空数组,并且每次为这个空数组加一长度然后作为递归的入参返回,一直到数组的长度和我们给的数字相同,这样就能够创建一个 N 长的元组类型了。
那么我们就能够根据这个思路,来实现一个 GetIntegerUnion 工具类型,上述的工具类型中每次都能够得到数组的长度,我们只要将这个长度拼凑起来成为一个联合类型,并且作为入参传递给下一次递归,这样就能够实现一开始的目标了。
type GetIntegerUnion<N extends number,R extends number = 0, Arr extends any[] = []>
= Arr['length'] extends N
? R
: GetIntegerUnion<N,R | Arr['length'] ,[any,...Arr]>
type AASSSS = GetIntegerUnion<3>[]
// type AASSSS = (0 | 1 | 2)[]
总结
本文属于一个小插曲,因为今天在闲逛的时候碰到了这个问题,所以想了个方法来进行解决,但是这个方法存在着一定的弊端,那就是 TS 中的递归是有次数限制的,那就是999次,所以这个方法没办法创建超过998的数字,如果有更好的方法的话,欢迎在评论区讨论。