玩转Typescript类型,告别any
本文适合对TS使用不熟练,对TS有疑惑的小伙伴阅读~
欢迎关注前端早茶,与广东靓仔携手共同进阶~
一、前言
二、正文
if 和 else
if
右边为 else
type A = 1
type B = 2
type Example = A extends B ? true : false // false
type Example = A extends B ? true : false
中的 true
和 false
即可以理解成它们分别为 if
分支和 else
分支中要写的代码
而 if
中的条件即为 A extends B
,A
是否可以分配给 B
要实现 else if
则需要多个这样的条件类型进行组合
模式匹配
type A = [1, 2, 3]
type ExampleA = A extends [infer First, ...infer Rest] ? First : never // 1
type B = "123"
type ExampleB = B extends `${infer FirstChar}${infer Rest}` ? FirstChar : never // '1'
模式匹配是我们要利用的最有用的 ts
特性之一,之后我们要实现的字符串的增删改查和元组的增删改查都要基于它
与或非
基于条件类型可以轻松实现与或非
// C 意为 Condition,条件
// common
// 与,即 C1,C2 同为真
type Andextends boolean, C2 extends boolean> = C1 extends true
? C2 extends true
? true
: false
: false
// common
// 与,即 C1,C2 有一个为真
type Orextends boolean, C2 extends boolean> = C1 extends true
? true
: C2 extends true
? true
: false
// common
// 非,即反转 C 的真假状态
type Notextends boolean> = C extends true ? false : true
ts
目前不支持动态个数的泛型参数,因此如果有多个条件,我们需要定义多个不同的,比如
// common
// 有三个条件的情况
type And3extends boolean, C2 extends boolean, C3 extends boolean> = And<
And,
C3
>
// common
// 有四个条件的情况
type And4<
C1 extends boolean,
C2 extends boolean,
C3 extends boolean,
C4 extends boolean
> = And, C4>
现在,我们已经封装了若干个类型工具 And Or Not
,要达成基于 ts
的类型系统实现加法器的目标
我们需要很多个这样的类型工具
为了方便管理,我们需要给它分模块,比如上面的与或非,我们划分在 common
里
我们还需要 function、array、number、object、string
这另外的五个,用于处理函数类型、元组类型、数字类型、对象类型、字符类型
判断相等
在 js 的运算操作符中,有 ==
和 ===
在 ts 类型系统中,也可以实现类似的判断
// common
// 判断左侧类型是否可以分配给右侧类型
type CheckLeftIsExtendsRightextends any, R extends any> = T extends R
? true
: false
// common
// 判断左侧类型是否和右侧类型一致
type IsEqual = (() => T extends A ? 1 : 2 ) extends <
T1
>() => T1 extends B ? 1 : 2
? true
: false
CheckLeftIsExtendsRight
即校验左侧类型是否可分配给右侧类型,和 ==
不同的是,==
会进行类型转换后的比较,而条件类型 Left extends Right ? xxx : xxx
只会进行结构性兼容的检查
如
type Example1 = { a: 1; b: 2 } extends { a: 1 } ? true : false // true
type Example2 = 1 | 2 extends 1 ? true : false // true
虽然两个类型长的不一样,但是可以通过约束校验
toString
要实现 ts 的数学运算过于麻烦,因为数字是无法进行 infer 的,如何判断它(一个数字类型)为整型,浮点型?还是正数,或者负数?是不是仅仅有一个数字类型没有任何办法?
这都需要基于字符类型(或元组类型)的模式匹配
// string
// 将类型转为字符串有一定的限制,仅支持下面的类型
type CanStringified = string | number | bigint | boolean | null | undefined
// string
// 将支持的类型转化为字符串
type Stringifyextends CanStringified> = `${T}`
效果
type Example1 = Stringify<0> // "0"
type Example2 = Stringify<-1> // "-1"
type Example3 = Stringify<0.1> // "0.1"
type Example4 = Stringify<"0.2"> // "0.2"
循环
在 js 中,我们可以通过 for、while、do...while
等循环进行可迭代对象的遍历,这些都离不开一个东西,那就是循环条件
比如在 for 循环中 for (初始化; 条件; 循环后逻辑)
我们一般用一个变量 i
,每一次循环后进行自增,循环条件一般是将 i
与另一个数比较大小
那么我们还需要实现一个数字类型大小比较,数字类型累加的工具类型
ts
中的循环可以通过递归来实现,ts
的类型系统中不存在类型赋值的概念
type Example = 1
Example = 2 // 没有这种写法
只有通过每次递归时,把当前泛型参数处理后,当做下一次递归的泛型参数,终止递归时,返回当前某一个泛型参数的类型
通过一个最简单的递归类型例子来看一下这个过程
type Example<
C extends boolean = true,
Tuple extends unknown[] = [1]
> = C extends true ? Example<false, [...Tuple, 1]> : Tuple
type Result = Example // [1, 1]
// Example 的两个泛型参数
如上示例,Result
得到的类型是 [1, 1]
第一次:C 是默认类型 true, 则会走到 Example
,其中 [...Tuple, 1]
的结果为 [...[1], 1]
,即 [1, 1]
第二次:C 传入了 false,会走到 Tuple
,Tuple 的值为上次传入的值 [1, 1]
,最后的返回类型为 [1, 1]
除了递归,还有两种方式可以循环,一种是分布式条件类型,还有一种是映射类型,但是它们都很难传递类型
// 分布式条件类型,当泛型参数 T 为联合类型时,条件类型即为分布式条件类型,会将 T 中的每一项分别分发给 extends 进行比对
type Example1 = T extends number ? T : never
type Result1 = Example1<"1" | "2" | 3 | 4> // 3 | 4
// 映射类型,固定写法,in 操作符会分发 T 成为新对象类型的键
type Example2 = {
[Key in T]: Key
}
type Result2 = Example2<"1" | "2" | 3 | 4> // { 1: "1"; 2: "2"; 3: 3; 4: 4; }
基本的数学运算
判断正负
在部分场景下,我们可以兼容 number 类型的数字,也可以兼容 string 类型的数字,定义其为 NumberLike
type NumberLike = number | `${number}`
// 可分配给 NumberLike 的类型示例:number、`${number}`、1、-1、0.1、-0.1、"1"、"-1" 等
判断为 0
// N 意为 Number 数字
// number
// number类型是否为0,判断 N 是否可分配给 0 | "0"
type IsZeroextends NumberLike> = common.CheckLeftIsExtendsRight0 | "0">
// number
// number类型是否大于0,泛型类型有限制 NumberLike,所以它一定是个数或者由数字构成的字符串,将其转为字符串后,判断最前面是不是 -,如果不是,就是大于零
type IsOverZeroextends NumberLike> = IsZero extends true
? false
: common.CheckLeftIsExtendsRight<
string.Stringify extends `${"-"}${infer Rest}` ? Rest : never,
never
>
// number
// number类型是否小于0,对上面 IsOverZero 的结果取反
type IsLessZeroextends NumberLike> = common.Not>
两数相加
在上面 循环
章节,我们讲到了,可以通过递归传递修改后的泛型参数,来创建复杂的工具类型
此场景下,我们可以生成动态的类型,最常见的有这几种,元组类型,模板字符串类型,联合类型
而元组类型的长度是可以访问的 如 [0, 1, 2]['length']
结果为 3
,且元组类型是可以拼接的,如 [...[0, 1, 2], ...[0]]['length']
的长度为 4
那么我们可以动态生成两个指定长度的元组类型,然后拼接到一起,获取拼接后的元组长度,就可以得到正整数(和 0)的加法了
// array
// 构造长度一定(Length)的元组
type GetTupleextends number = 0> = GetTupleHelper
type GetTupleHelper<
Length extends number = 0,
R extends unknown[] = []
> = R["length"] extends Length ? R : GetTupleHelper
type IntAddSingleHeplerextends number, N2 extends number> = [
...array.GetTuple,
...array.GetTuple
]["length"]
// number
// 正整数(和0)加法,T1,T2最大999
type IntAddSingleextends number, N2 extends number> = IntAddSingleHepler<
N1,
N2
> extends number
? IntAddSingleHepler
: number
比较大小
如果想要实现元组类型的排序,那就必须要能够比较数字大小
如何实现数字类型大小的比较呢?
还是得基于元组
基于两个数 N1
、 N2
,创建不同的元组 T1
、T2
,依次减少两个元组的长度(删除第一位或最后一位),当有一个元组长度为 0 时,就是这个元组对应的数字类型,比另一个数字类型小(或相等,所以也要先判断是否不相等才进行比较)
基于模式匹配,匹配出最后一项,和剩余项,并返回剩余项
类型系统中没有改变原类型的概念,因此元组类型的增删改查都应该直接返回修改后的类型,而不是修改后的变化值
如在 js 中,[1, 2, 3].shift()
会返回 1
,[1, 2, 3].pop()
会返回 3
,但是 ts 类型系统中,这样返回是没有意义的,Pop<[1, 2, 3]>
应该得到类型 [1, 2]
// array
// 去掉数组的最后一位
type Popextends unknown[]> = T extends [...infer LeftRest, infer Last]
? LeftRest
: never
// T 意为 Tuple 元组
type CompareHelper<
N1 extends number,
N2 extends number,
T1 extends unknown[] = array.GetTuple,
T2 extends unknown[] = array.GetTuple
> = IsNotEqualtrue> extends true
? common.Or"length"]>, IsZero"length"]>> extends true
? IsZero"length"]> extends true
? false
: true
: CompareHelper["length"], array.Pop["length"]>
: false
// number
// 比较两个数字类型大小
type Compareextends number, N2 extends number> = CompareHelper
两数相减
两个数字类型相减的逻辑与两个数字类型比较大小的逻辑类似,但是返回类型时,会返回剩余长度多的元组的长度
这个实现受到元组类型长度的限制,只能得到正数(或 0),即结果的绝对值
且用在其他工具类型中时,会出现 类型实例化过深,并可能无限(Type instantiation is excessively deep and possibly infinite)
的报错
即:目前有 50 个嵌套实例的限制,可以通过批处理规避限制
// 批处理示例
type GetLetters =
Text extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer Rest}`
? C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | GetLetters
: Text extends `${infer C}${infer Rest}`
? C | GetLetters
: never
减法实现
type IntMinusSingleAbsHelper<
N1 extends number,
N2 extends number,
T1 extends unknown[] = array.GetTuple,
T2 extends unknown[] = array.GetTuple
> = IsNotEqualtrue> extends true
? common.Or"length"]>, IsZero"length"]>> extends true
? IsZero"length"]> extends true
? T2["length"]
: T1["length"]
: IntMinusSingleAbsHelper["length"], array.Pop["length"]>
: 0
// number
// 两个数字类型相减,得到绝对值
type IntMinusSingleAbs<
N1 extends number,
N2 extends number
> = IntMinusSingleAbsHelper
虽然有嵌套深度的限制,写好的减法不能用,但是加法是很好用的,有加法我们一样可以写出很多逻辑
三、参考 js 封装工具类型
CharAt - 获取字符串在索引位 I 下的 字符
/**
* 获取字符串在索引位 I 下的 字符
* @example
* type Result = CharAt<"123", 1> // "2"
*/
type CharAtextends string, I extends number> = Split[I]
原理:元组类型可以进行索引访问,可以将字符串类型按照 ''
分割成元组类型,然后通过 索引访问
,得到索引位 I
处的字符
Concat - 拼接两个字符串
/**
* 拼接两个字符串
* @example
* type Result = Concat<"123", "456"> // "123456"
*/
type Concatextends 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,
Len2 extends number = GetStringLength
> = common.Or<
number.Compare,
number.IsEqual
> extends true
? S1 extends `${infer Left}${S2}${infer Right}`
? GetStringLength
: -1
: -1
/**
* 从左往右查找子串的位置
* @example
* type Result = IndexOf<"123", "23"> // 1
*/
type IndexOfextends string, S2 extends string> = IndexOfHelper
原理:匹配出 ${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"">,
S2,
number.IntAddSingle, AddOffset>,
number.IntAddSingle>
>
: Index
/**
* 从右往左查找子串的位置
* @example
* type Result = LastIndexOf<"23123", "23"> // 3
*/
type LastIndexOfextends 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 extends true
? ReplaceAll, MatchStr, ReplaceStr>
: S
Replace
,递归进行替换,替换掉所有 MatchStr
,终止条件是 S
是否包含 MatchStr
Trim - 去掉字符串的空格
/**
* 去掉字符串类型两侧的空格
* @example
* type Result = PadStart<' 0123 '> // '0123'
*/
type Trimextends string> = TrimLeft>
TrimRight
去掉右侧的空格,再把前者结果交给 TrimLeft
去掉左侧的空格ToUpperCase - 字符串转大写
/**
* 字符串转大写
* @example
* type Result = ToUpperCase<'abc'> // 'ABC'
*/
type ToUpperCaseextends string> = Uppercase
ToLowerCase - 字符串转小写
/**
* 字符串转小写
* @example
* type Result = ToUpperCase<'ABC'> // 'abc'
*/
type ToLowerCaseextends string> = Lowercase
SubString - 截取 start(包括)到 end(不包括)之间的字符串
type SubStringHelper<
S extends string,
Start extends number,
End extends number,
Offset extends number = 0,
Cache extends string[] = []
> = number.IsEqual extends true
? array.Join"">
: SubStringHelper<
S,
Start,
End,
number.IntAddSingle1>,
common.And3<
common.Or<number.Compare, number.IsEqual>,
common.Or<number.Compare, number.IsEqual>,
CharAt extends string ? true : false
> extends true
? array.Push>
: Cache
>
/**
* 截取start(包括)到end(不包括)之间的字符串
* @example
* type Result = SubString<'123', 0, 1> // '1'
*/
type SubString<
S extends string,
Start extends number,
End extends number
> = SubStringHelper
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
> = SubStringHelpernumber.IntAddSingle>
SubString
需要起始和结束,有 Start
和 Len
就可以先算出 End
,就可以使用 SubString
了Pop - 去除元组类型的最后一位
/**
* 去掉元组的最后一位
* @see https://juejin.cn/post/7045536402112512007#heading-2
* @example
* type Result = Pop<[1, 2, 3]> // [1, 2]
*/
type Popextends unknown[]> = T extends [...infer LeftRest, infer Last]
? LeftRest
: never
Shift - 去除元组类型的第一位
/**
* 去掉数组的第一位
* @example
* type Result = Shift<[1, 2, 3]> // [2, 3]
*/
type Shiftextends unknown[]> = T extends [infer First, ...infer RightRest]
? RightRest
: never
原理:与 Pop
同理
UnShift - 在元组前面插入一位
/**
* 在元组前面插入一位
* @example
* type Result = UnShift<[1, 2, 3], 0> // [0, 1, 2, 3]
*/
type UnShiftextends unknown[], Item> = [Item, ...T]
[]
中直接写类型可以构建新元组类型,其中写 ...Tuple
,与 js 中的扩展运算符效果一致Push - 在元组后面插入一位
/**
* 在元组最后插入一位
* @example
* type Result = Push<[1, 2, 3], 4> // [1, 2, 3, 4]
*/
type Pushextends unknown[], Item> = [...T, Item]
UnShift
Concat - 合并两个元组类型
/**
* 合并两个元组类型
* @example
* type Result = Concat<[1, 2, 3], [4]> // [1, 2, 3, 4]
*/
type Concatextends unknown[], R extends unknown[]> = [...T, ...R]
UnShift
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.IntAddSingle1>,
common.And, CacheBool>
>
/**
* 校验元组中每个类型是否都符合条件
* @example
* type Result = Every<[1, 2, 3], number> // true
*/
type Everyextends unknown[], Check> = T["length"] extends 0
? false
: EveryHelper
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.IntAddSingle1>,
common.Or, CacheBool>
>
/**
* 校验元组中是否有类型符合条件
* @example
* type Result = Every<['1', '2', 3], number> // true
*/
type Someextends unknown[], Check> = SomeHelper
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 extends true /** any[] -> T[] */
? T
: F[]
: FillHelper, F>, F, number.IntAddSingle1>>
/**
* 以指定类型填充元组类型
* @example
* type Result = Fill<['1', '2', 3, any], 1> // [1, 1, 1, 1]
*/
type Fillextends unknown[], F = undefined> = FillHelper
原理:如果原元组长度为 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.IntAddSingle1>,
common.And> extends true
? array.Push
: common.And<
common.Not,
common.CheckLeftIsExtendsRight
> extends true
? array.Push
: Cache
>
/**
* 过滤出元组类型中符合条件的类型
* @example
* type Result = Filter<['1', '2', 3, any, 1], 1, true> // [1]
*/
type Filter<
T extends unknown[],
C,
Strict extends boolean = false
> = FilterHelper
原理:严格模式,即 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"length"], 1>
? null
: common.CheckLeftIsExtendsRight extends true
? T[Offset]
: FindHelpernumber.IntAddSingle1>>
/** */
type Findextends unknown[], C> = FindHelper
null
类型Reverse - 反转元组
type ReverseHelper<
T extends unknown[],
Offset extends number = 0,
Cache extends unknown[] = []
> = Cache["length"] extends T["length"]
? Cache
: ReverseHelpernumber.IntAddSingle1>, UnShift>
/** */
type Reverseextends unknown[]> = ReverseHelper
Cache
的前面插入当前类型FindIndex - 找到元组类型中第一个符合条件的类型的索引
type FindIndexHelper<
T extends unknown[],
C,
Strict extends boolean = false,
Offset extends number = 0
> = Offset extends number.IntAddSingle"length"], 1>
? -1
: common.And, Strict> extends true
? Offset
: common.And<
common.CheckLeftIsExtendsRight,
common.Not
> extends true
? Offset
: FindIndexHelpernumber.IntAddSingle1>>
/** */
type FindIndex<
T extends unknown[],
C,
Strict extends boolean = false
> = FindIndexHelper
Filter
,遍历元组,符合约束校验时,返回当前 Offset
,否则结束后返回 -1Flat - 扁平化元组
type FlatHelper<
T extends unknown[],
Offset extends number = 0,
Cache extends unknown[] = []
> = Offset extends T["length"]
? Cache
: FlatHelper<
T,
number.IntAddSingle1>,
T[Offset] extends unknown[]
? Concat
: Push
>
type Flatextends unknown[]> = FlatHelper
unknown[]
的约束,则将它 Push
进新元组中,否则将它 Concat
进去Includes - 元组类型中是否存在一个符合条件的类型
type Includesextends unknown[], C> = common.CheckLeftIsExtendsRight<
C,
TupleToUnion
>
原理:将元组转为联合类型,如果约束条件 C
可以分配给该联合类型,那么就是 true
Slice - 提取元组类型中指定起始位置到指定结束位置的类型构造新元组类型
type SliceHelper<
T extends unknown[],
Start extends number,
End extends number,
Offset extends number = 0,
Cache extends unknown[] = []
> = number.IsEqual extends true
? Cache
: SliceHelper<
T,
Start,
End,
number.IntAddSingle1>,
common.And3<
common.Or<number.Compare, number.IsEqual>,
common.Or<number.Compare, number.IsEqual>,
common.Or<
number.Compare"length"], Offset>,
number.IsEqual"length"], End>
>
> extends true
? array.Push
: Cache
>
type Slice<
T extends unknown[],
Start extends number,
End extends number
> = SliceHelper
原理:和字符串裁剪的类似,遍历老元组,当循环条件 Offset
大于等于 Start
或 小于等于 End
时,将这些类型 Push
到新元组中
Sort - 排序
type SortHepler2<
T extends number[],
Offset extends number = 0,
Offset1 extends number = 0,
Offset1Added extends number = number.IntAddSingle1>,
Seted1 extends unknown[] = ArraySet,
Seted2 extends unknown[] = ArraySet
> = number.IntAddSingle<
number.IntAddSingle,
1
> extends T["length"]
? SortHepler1number.IntAddSingle1>>
: SortHepler2<
number.Compare extends true
? Seted2 extends number[]
? Seted2
: never
: T,
number.IntAddSingle1>
>
type SortHepler1<
T extends number[],
Offset extends number = 0
> = Offset extends T["length"] ? T : SortHepler2
type Sortextends number[]> = SortHepler1
原理:最简单的冒泡排序,每次排序时,将大的与小的置换
面试题库推荐
四、最后
这是类型的具体操作,弄清楚它的原理对ts的某一方面有更高的理解,可以了解一下,之后在日常开发TS的时候会更佳得心应手~
关注我,一起携手进阶
欢迎关注前端早茶,与广东靓仔携手共同进阶