要实现 ts 的数学运算过于麻烦,因为数字是无法进行 infer 的,如何判断它(一个数字类型)为整型,浮点型?还是正数,或者负数?是不是仅仅有一个数字类型没有任何办法?
CharAt - 获取字符串在索引位 I 下的 字符
/**
* 获取字符串在索引位 I 下的 字符
* @example
* type Result = CharAt<"123", 1> // "2"
*/
type CharAt<S extends string, I extends number> = Split<S>[I]
原理:元组类型可以进行索引访问,可以将字符串类型按照 ''
分割成元组类型,然后通过 索引访问
,得到索引位 I
处的字符
Concat - 拼接两个字符串
/**
* 拼接两个字符串
* @example
* type Result = Concat<"123", "456"> // "123456"
*/
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`
Includes - 判断字符串是否包含子串
/**
* 判断字符串是否包含子串
* @example
* type Result = Includes<"123", "12"> // true
*/
type Includes<
S1 extends string,
S2 extends string
> = S1 extends `${infer Left}${S2}${infer Right}` ? true : false
IndexOf - 从左往右查找子串的位置
type IndexOfHelper<
S1 extends string,
S2 extends string,
Len1 extends number = GetStringLength<S1>,
Len2 extends number = GetStringLength<S2>
> = common.Or<
number.Compare<Len1, Len2>,
number.IsEqual<Len1, Len2>
> extends true
? S1 extends `${infer Left}${S2}${infer Right}`
? GetStringLength<Left>
: -1
: -1
/**
* 从左往右查找子串的位置
* @example
* type Result = IndexOf<"123", "23"> // 1
*/
type IndexOf<S1 extends string, S2 extends string> = IndexOfHelper<S1, S2>
原理:匹配出 ${infer Left}${S2}${infer Right}
中 Left
,求其长度,则索引位即为 Left
的长度,如果匹配不到,返回 -1
可以先比较父串和子串的长度,如果子串比父串还长,那就不需要匹配了,直接返回 -1
LastIndexOf - 从右往左查找子串的位置
type LastIndexOfHelper<
S1 extends string,
S2 extends string,
Index extends number = -1 /** 当前从左往右匹配最大的值,匹配不到以后,上一次匹配的索引就是从右往左第一个的索引 */,
AddOffset extends number = 0 /** 每次从左往右匹配并替换成空串后,下次循序需要累加的值 */
> = S1 extends `${infer Left}${S2}${infer Right}`
? LastIndexOfHelper<
Replace<S1, S2, "">,
S2,
number.IntAddSingle<GetStringLength<Left>, AddOffset>,
number.IntAddSingle<AddOffset, GetStringLength<S2>>
>
: Index
/**
* 从右往左查找子串的位置
* @example
* type Result = LastIndexOf<"23123", "23"> // 3
*/
type LastIndexOf<S1 extends string, S2 extends string> = LastIndexOfHelper<
S1,
S2
>
原理:模板字符串类型的模式匹配是从左往右的,而 LastIndexOf
是从右往左的,所以在匹配时,仍然基于从左往右匹配,但是每次匹配后,替换掉匹配过的子串为空串
然后把删掉的部分的长度累计起来,结果就是模拟从右往左匹配到的索引值
Replace - 在字符串中查找并替换一处子串
/**
* 在字符串中查找并替换一处子串
* @example
* type Result = Replace<"23123", "23", "xx"> // "xx123"
*/
type Replace<
S extends string,
MatchStr extends string,
ReplaceStr extends string
> = S extends `${infer Left}${MatchStr}${infer Right}`
? `${Left}${ReplaceStr}${Right}`
: S
原理:基于模板字符串的模式匹配,匹配到了就用
ReplaceStr
换掉
MatchStr
ReplaceAll - 在字符串中查找并替换所有子串
/**
* 在字符串中查找并替换所有子串
* @example
* type Result = Replace<"23123", "23", "xx"> // "xx1xx"
*/
type ReplaceAll<
S extends string,
MatchStr extends string,
ReplaceStr extends string
> = Includes<S, MatchStr> extends true
? ReplaceAll<Replace<S, MatchStr, ReplaceStr>, MatchStr, ReplaceStr>
: S
原理:基于
Replace
,递归进行替换,替换掉所有
MatchStr
,终止条件是
S
是否包含
MatchStr
Trim - 去掉字符串的空格
/**
* 去掉字符串类型两侧的空格
* @example
* type Result = PadStart<' 0123 '> // '0123'
*/
type Trim<S extends string> = TrimLeft<TrimRight<S>>
原理:先用
TrimRight
去掉右侧的空格,再把前者结果交给
TrimLeft
去掉左侧的空格
ToUpperCase - 字符串转大写
/**
* 字符串转大写
* @example
* type Result = ToUpperCase<'abc'> // 'ABC'
*/
type ToUpperCase<S extends string> = Uppercase<S>
ToLowerCase - 字符串转小写
/**
* 字符串转小写
* @example
* type Result = ToUpperCase<'ABC'> // 'abc'
*/
type ToLowerCase<S extends string> = Lowercase<S>
SubString - 截取 start(包括)到 end(不包括)之间的字符串
type SubStringHelper<
S extends string,
Start extends number,
End extends number,
Offset extends number = 0,
Cache extends string[] = []
> = number.IsEqual<Offset, End> extends true
? array.Join<Cache, "">
: SubStringHelper<
S,
Start,
End,
number.IntAddSingle<Offset, 1>,
common.And3<
common.Or<number.Compare<Offset, Start>, number.IsEqual<Offset, Start>>,
common.Or<number.Compare<End, Offset>, number.IsEqual<Offset, End>>,
CharAt<S, Offset> extends string ? true : false
> extends true
? array.Push<Cache, CharAt<S, Offset>>
: Cache
>
/**
* 截取start(包括)到end(不包括)之间的字符串
* @example
* type Result = SubString<'123', 0, 1> // '1'
*/
type SubString<
S extends string,
Start extends number,
End extends number
> = SubStringHelper<S, Start, End>
原理:遍历字符串类型的每一个字符,如果当前索引大于等于
Start
,并且小于等于
End
,就把当前字符 push 到元组中,最后用
array.Join
,将元组转为字符串类型
SubStr - 在字符串中抽取从开始下标到结束下标的字符
/**
* 在字符串中抽取从 开始 下标开始的指定数目的字符
* @example
* type Result = SubStr<'123', 1, 2> // '23'
*/
type SubStr<
S extends string,
Start extends number,
Len extends number
> = SubStringHelper<S, Start, number.IntAddSingle<Start, Len>>
原理:
SubString
需要起始和结束,有
Start
和
Len
就可以先算出
End
,就可以使用
SubString
了
Pop - 去除元组类型的最后一位
/**
* 去掉元组的最后一位
* @see https://juejin.cn/post/7045536402112512007#heading-2
* @example
* type Result = Pop<[1, 2, 3]> // [1, 2]
*/
type Pop<T extends unknown[]> = T extends [...infer LeftRest, infer Last]
? LeftRest
: never
原理:基于元组的模式匹配,提取最后一项,返回剩余项
Shift - 去除元组类型的第一位
/**
* 去掉数组的第一位
* @example
* type Result = Shift<[1, 2, 3]> // [2, 3]
*/
type Shift<T extends unknown[]> = T extends [infer First, ...infer RightRest]
? RightRest
: never
原理:与 Pop
同理
UnShift - 在元组前面插入一位
/**
* 在元组前面插入一位
* @example
* type Result = UnShift<[1, 2, 3], 0> // [0, 1, 2, 3]
*/
type UnShift<T extends unknown[], Item> = [Item, ...T]
原理:
[]
中直接写类型可以构建新元组类型,其中写
...Tuple
,与 js 中的扩展运算符效果一致
Push - 在元组后面插入一位
/**
* 在元组最后插入一位
* @example
* type Result = Push<[1, 2, 3], 4> // [1, 2, 3, 4]
*/
type Push<T extends unknown[], Item> = [...T, Item]
Concat - 合并两个元组类型
/**
* 合并两个元组类型
* @example
* type Result = Concat<[1, 2, 3], [4]> // [1, 2, 3, 4]
*/
type Concat<T extends unknown[], R extends unknown[]> = [...T, ...R]
Join - 将元组类型拼接成字符串类型
/**
* 将元组类型拼接成字符串类型
* @example
* type Result = Join<[1, 2, 3]> // "1,2,3"
*/
type Join<
T extends string.CanStringified[],
SplitStr extends string.CanStringified = ""
> = T["length"] extends 0
? ""
: T extends [infer Left, ...infer RightRest]
? Left extends string.CanStringified
? RightRest extends string.CanStringified[]
? `${Left}${T["length"] extends 1 ? "" : SplitStr}${Join<
RightRest,
SplitStr
>}`
: never
: never
: never
原理:每次递归时提取元组第一个类型,然后将此类型放到模板字符串类型的第一个位置 ${第一个位置}${第二个位置}${第三个位置}
第二个位置即转为字符串用来分隔的子串,如果元组的长度为 0,则为空串
第三个位置则是剩下部分的逻辑,即重复最开始的逻辑
Every - 校验元组中每个类型是否都符合条件
type EveryHelper<
T extends unknown[],
Check,
Offset extends number = 0,
CacheBool extends boolean = true
> = T["length"] extends Offset
? CacheBool
: EveryHelper<
T,
Check,
number.IntAddSingle<Offset, 1>,
common.And<common.CheckLeftIsExtendsRight<T[Offset], Check>, CacheBool>
>
/**
* 校验元组中每个类型是否都符合条件
* @example
* type Result = Every<[1, 2, 3], number> // true
*/
type Every<T extends unknown[], Check> = T["length"] extends 0
? false
: EveryHelper<T, Check>
原理:初始类型
CacheBool
为 true,依次将元组中每个类型与初始类型进行
与
操作,如果元组长度为 0,则返回
false
Some - 校验元组中是否有类型符合条件
type SomeHelper<
T extends unknown[],
Check,
Offset extends number = 0,
CacheBool extends boolean = false
> = T["length"] extends Offset
? CacheBool
: SomeHelper<
T,
Check,
number.IntAddSingle<Offset, 1>,
common.Or<common.CheckLeftIsExtendsRight<T[Offset], Check>, CacheBool>
>
/**
* 校验元组中是否有类型符合条件
* @example
* type Result = Every<['1', '2', 3], number> // true
*/
type Some<T extends unknown[], Check> = SomeHelper<T, Check>
原理:初始类型
CacheBool
为 false,依次将元组中每个类型与初始类型进行
或
操作,如果元组长度为 0,则返回
false
Fill - 以指定类型填充元组类型
type FillHelper<
T extends unknown[],
F,
Offset extends number = 0
> = T["length"] extends 0
? F[]
: Offset extends T["length"]
? common.IsEqual<T, F[]> extends true /** any[] -> T[] */
? T
: F[]
: FillHelper<array.Push<array.Shift<T>, F>, F, number.IntAddSingle<Offset, 1>>
/**
* 以指定类型填充元组类型
* @example
* type Result = Fill<['1', '2', 3, any], 1> // [1, 1, 1, 1]
*/
type Fill<T extends unknown[], F = undefined> = FillHelper<T, F>
原理:如果原元组长度为 0,则直接返回由新类型构成的元组 F[]
如果是数组类型如:any[]
、never[]
、number[]
,也应该直接替换成 T[]
否则,每次在原元组中删除第一个,然后在最前面添加一个新类型,直到循环条件与
T
的长度一致时,终止递归
Filter - 过滤出元组类型中符合条件的类型
type FilterHelper<
T extends unknown[],
C,
Strict extends boolean,
Offset extends number = 0,
Cache extends unknown[] = []
> = Offset extends T["length"]
? Cache
: FilterHelper<
T,
C,
Strict,
number.IntAddSingle<Offset, 1>,
common.And<Strict, common.IsEqual<T[Offset], C>> extends true
? array.Push<Cache, T[Offset]>
: common.And<
common.Not<Strict>,
common.CheckLeftIsExtendsRight<T[Offset], C>
> extends true
? array.Push<Cache, T[Offset]>
: Cache
>
/**
* 过滤出元组类型中符合条件的类型
* @example
* type Result = Filter<['1', '2', 3, any, 1], 1, true> // [1]
*/
type Filter<
T extends unknown[],
C,
Strict extends boolean = false
> = FilterHelper<T, C, Strict>
原理:严格模式,即 any
只能为 any
,而不能为 1
、unknown
这样的其他类型
如果是严格模式就用 common.IsEqual
进行约束校验,否则用 common.CheckLeftIsExtendsRight
进行约束校验
每次递归时,如果满足上述条件,则放入新的元组类型中
如果循环条件 Offset
等于 T
的长度是,终止循环,返回新的元组类型 Cache
Find - 找到元组类型中第一个符合条件的类型
type FindHelper<
T extends unknown[],
C,
Offset extends number = 0
> = Offset extends number.IntAddSingle<T["length"], 1>
? null
: common.CheckLeftIsExtendsRight<T[Offset], C> extends true
? T[Offset]
: FindHelper<T, C, number.IntAddSingle<Offset, 1>>
/** */
type Find<T extends unknown[], C> = FindHelper<T, C>
原理:遍历在元组中找,如果找到了匹配的,则返回该类型,否则返回
null
类型
Reverse - 反转元组
type ReverseHelper<
T extends unknown[],
Offset extends number = 0,
Cache extends unknown[] = []
> = Cache["length"] extends T["length"]
? Cache
: ReverseHelper<T, number.IntAddSingle<Offset, 1>, UnShift<Cache, T[Offset]>>
/** */
type Reverse<T extends unknown[]> = ReverseHelper<T>
原理:遍历老元组类型,每次在新元组类型
Cache
的前面插入当前类型
FindIndex - 找到元组类型中第一个符合条件的类型的索引
type FindIndexHelper<
T extends unknown[],
C,
Strict extends boolean = false,
Offset extends number = 0
> = Offset extends number.IntAddSingle<T["length"], 1>
? -1
: common.And<common.IsEqual<T[Offset], C>, Strict> extends true
? Offset
: common.And<
common.CheckLeftIsExtendsRight<T[Offset], C>,
common.Not<Strict>
> extends true
? Offset
: FindIndexHelper<T, C, Strict, number.IntAddSingle<Offset, 1>>
/** */
type FindIndex<
T extends unknown[],
C,
Strict extends boolean = false
> = FindIndexHelper<T, C, Strict>
原理:严格模式,参考上文
Filter
,遍历元组,符合约束校验时,返回当前
Offset
,否则结束后返回 -1
Flat - 扁平化元组
type FlatHelper<
T extends unknown[],
Offset extends number = 0,
Cache extends unknown[] = []
> = Offset extends T["length"]
? Cache
: FlatHelper<
T,
number.IntAddSingle<Offset, 1>,
T[Offset] extends unknown[]
? Concat<Cache, T[Offset]>
: Push<Cache, T[Offset]>
>
type Flat<T extends unknown[]> = FlatHelper<T>
原理:遍历元组类型,如果当前类型不满足
unknown[]
的约束,则将它
Push
进新元组中,否则将它
Concat
进去
Includes - 元组类型中是否存在一个符合条件的类型
type Includes<T extends unknown[], C> = common.CheckLeftIsExtendsRight<
C,
TupleToUnion<T>
>
原理:将元组转为联合类型,如果约束条件 C
可以分配给该联合类型,那么就是 true
Slice - 提取元组类型中指定起始位置到指定结束位置的类型构造新元组类型
type SliceHelper<
T extends unknown[],
Start extends number,
End extends number,
Offset extends number = 0,
Cache extends unknown[] = []
> = number.IsEqual<Offset, End> extends true
? Cache
: SliceHelper<
T,
Start,
End,
number.IntAddSingle<Offset, 1>,
common.And3<
common.Or<number.Compare<Offset, Start>, number.IsEqual<Offset, Start>>,
common.Or<number.Compare<End, Offset>, number.IsEqual<Offset, End>>,
common.Or<
number.Compare<T["length"], Offset>,
number.IsEqual<T["length"], End>
>
> extends true
? array.Push<Cache, T[Offset]>
: Cache
>
type Slice<
T extends unknown[],
Start extends number,
End extends number
> = SliceHelper<T, Start, End>
原理:和字符串裁剪的类似,遍历老元组,当循环条件 Offset
大于等于 Start
或 小于等于 End
时,将这些类型 Push
到新元组中
Sort - 排序
type SortHepler2<
T extends number[],
Offset extends number = 0,
Offset1 extends number = 0,
Offset1Added extends number = number.IntAddSingle<Offset1, 1>,
Seted1 extends unknown[] = ArraySet<T, Offset1Added, T[Offset1]>,
Seted2 extends unknown[] = ArraySet<Seted1, Offset1, T[Offset1Added]>
> = number.IntAddSingle<
number.IntAddSingle<Offset, Offset1>,
1
> extends T["length"]
? SortHepler1<T, number.IntAddSingle<Offset, 1>>
: SortHepler2<
number.Compare<T[Offset1], T[Offset1Added]> extends true
? Seted2 extends number[]
? Seted2
: never
: T,
number.IntAddSingle<Offset1, 1>
>
type SortHepler1<
T extends number[],
Offset extends number = 0
> = Offset extends T["length"] ? T : SortHepler2<T, Offset>
type Sort<T extends number[]> = SortHepler1<T>
原理:最简单的冒泡排序,每次排序时,将大的与小的置换
文章转载于:https://juejin.cn/post/7061556434692997156