TS试炼-7
摘要:
包含:AllCombinations(全组合)、Greater Than(大于)、Zip(元组相同下标合并)、IsTuple(判断是否为元组)、Chunk(切片)、Fill(填充)、Trim Right(去除右侧空格)、Without(去除数组指定元素)、Trunc(取整)、IndexOf(获取索引)
1、AllCombinations(全组合)
ts
type AllCombinations_ABC = AllCombinations<'ABC'>;
// should be '' | 'A' | 'B' | 'C' | 'AB' | 'AC' | 'BA' | 'BC' | 'CA' | 'CB' | 'ABC' | 'ACB' | 'BAC' | 'BCA' | 'CAB' | 'CBA'
第一种方案
ts
type StringToUnion<S extends string> = S extends `${infer First}${infer Rest}` ? First | StringToUnion<Rest> : never;
type AllCombinations<S extends string, T extends string = StringToUnion<S>> = [T] extends [never]
? ''
: '' | { [K in T]: `${K}${AllCombinations<never, Exclude<T, K>>}` }[T];
解释:
ts
type A = '' | { A: 'A' }['A']; // '' | 'A'
type B = '' | { B: 'B' }['B']; // '' | 'B'
type C = '' | { C: 'C' }['C']; // '' | 'C'
type AB = '' | { A: `A${B}`, B: `B${A}` }['A' | 'B']; // '' | 'A' | 'AB' | 'B' | 'BA'
type BC = '' | { B: `B${C}`, C: `C${B}` }['B' | 'C']; // '' | 'B' | 'BC' | 'C' | 'CB'
type AC = '' | { A: `A${C}`, C: `C${A}` }['A' | 'C']; // '' | 'A' | 'AC' | 'C' | 'CA'
type ABC = '' | {
A: `A${BC}`;
B: `B${AC}`;
C: `C${AB}`;
}['A' | 'B' | 'C'];
// '' | 'A' | 'B' | 'C' | 'AB' | 'AC' | 'BA' | 'BC' | 'CA' | 'CB' | 'ABC' | 'ACB' | 'BAC' | 'BCA' | 'CAB' | 'CBA'
// 相当于
type ABC = '' | {
A: 'A' | 'AB' | 'ABC' | 'AC' | 'ACB';
B: 'B' | 'BA' | 'BAC' | 'BC' | 'BCA';
C: 'C' | 'CA' | 'CAB' | 'CB' | 'CBA';
}['A' | 'B' | 'C'];
第二种方案
ts
// StringToUnion同上
// 通过判断联合类型为never结束
type AllCombinations<
S extends string,
T extends string = StringToUnion<S>,
U extends string = T,
> = [T] extends [never]
? ''
: T extends T
? '' | `${T}${AllCombinations<never, Exclude<U, T>>}`
: never;
// 通过判断字符串来结束
type AllCombinations<
S extends string,
T extends string = StringToUnion<S>,
U extends string = T,
> = S extends `${infer F}${infer R}`
? U extends U
? '' | `${U}${AllCombinations<R, Exclude<T, U>>}`
: never
: '';
// 精简一下
type AllCombinations<
S extends string,
T extends string = StringToUnion<S> | '',
U extends string = T,
> = T extends T ? '' | `${T}${AllCombinations<never, Exclude<U, T>>}` : never;
// 同上
type AllCombinations<
S extends string,
T extends string = StringToUnion<S> | '',
U extends string = T,
> = T extends T ? `${T}${AllCombinations<never, Exclude<U, T>> | ''}` : never;
解释:
原理相同,只是第一种采用属性,第二种采用联合类型遍历。
上两种方案,都采用前面联合一个空字符串来实现全组合,如果只要全排列,就不需要联合空字符串了(精简版本不行)
扩展:
字符串全排列(只需要去除空字符串的联合即可)
ts
// 'ABC' -> "ABC" | "BCA" | "BAC" | "CBA" | "CAB" | "ACB"
// 通过属性
type StringPermutation<S extends string, T extends string = StringToUnion<S>> = [T] extends [never]
? ''
: { [K in T]: `${K}${StringPermutation<never, Exclude<T, K>>}` }[T];
// 通过联合遍历
type StringPermutation<
S extends string,
T extends string = StringToUnion<S>,
U extends string = T,
> = [T] extends [never]
? ''
: T extends T
? `${T}${StringPermutation<never, Exclude<U, T>>}`
: never;
联合类型全排列
ts
// 'A' | 'B' | 'C' -> ["A", "B", "C"] | ["B", "C", "A"] | ["B", "A", "C"] | ["C", "B", "A"] | ["C", "A", "B"] | ["A", "C", "B"]
type UnionPermutation<T, U = T> = [T] extends [never] ? [] : T extends U ? [T, ...UnionPermutation<Exclude<U, T>>] : [];
元组全排列
ts
// ['A', 'B', 'C'] -> ["A", "B", "C"] | ["B", "C", "A"] | ["B", "A", "C"] | ["C", "B", "A"] | ["C", "A", "B"] | ["A", "C", "B"]
// 使用上面联合类型的全排列
type TuplePermutation<S extends unknown[]> = UnionPermutation<S[number]>;
// 或者
type TuplePermutation<S extends unknown[], T = S[number], U = T> = [T] extends [never]
? []
: T extends T
? [T, ...TuplePermutation<[], Exclude<U, T>>]
: never;
总结
只要涉及到排列或者组合的,首先考虑转换为联合类型,使用联合类型的遍历,实现相关功能。
2、Greater Than(大于)
ts
GreaterThan<2, 1> //should be true
GreaterThan<1, 1> //should be false
GreaterThan<10, 100> //should be false
GreaterThan<111, 11> //should be true
实现
ts
// 数字转指定长度的元组
type NumberToTuple<T extends number | string, R extends 0[] = []> = `${R['length']}` extends `${T}` ? R : NumberToTuple<T, [...R, 0]>;
// 长度转指定长度的元组
type LengthToTuple<T extends number | string, R extends 0[] = []> = `${T}` extends `${infer _}${infer rest}` ? LengthToTuple<rest, [...R, 0]> : R;
// 比较两个元组的大小
type CompareTuple<T extends 0[], U extends 0[]> = T extends [...U, ...infer _] ? U extends T ? 0 : true : false;
// 比较两个数字的大小(因元组大小限制,此方法最多支持1000以内的比较)
type CompareNumber<T extends number | string, U extends number | string> = CompareTuple<NumberToTuple<T>, NumberToTuple<U>>;
// 判断是否大于
type GreaterThan<
T extends number | string,
U extends number | string,
L = CompareTuple<LengthToTuple<T>, LengthToTuple<U>>, // 比较长度
> = L extends boolean
? L // 长度不同时,直接返回长度比较结果
: [`${T}`, `${U}`] extends [`${infer TF}${infer TR}`, `${infer UF}${infer UR}`]
? CompareNumber<TF, UF> extends true // 相同位数比较
? true // 相同位数大于:大于
: GreaterThan<TR, UR> // 相同位数不大于时,比较剩余位数
: false; // 全部位数比较完都不大于,返回结果:不大于
解析:
思路:通过往元组添加指定位数项,判断继承关系,从而判断大小。
1、先比较位数,位数大则大,位数小则小,位数相同在对每一位进行比较
2、某一位大则返回大,不大则比较剩余位数
3、全部位数比较完,都没有大于的,则返回不大于。
3、Zip(元组相同下标合并)
ts
type exp = Zip<[1, 2], [true, false]> // expected to be [[1, true], [2, false]]
type exp = Zip<[1, 2, 3], ['1', '2']> // expected to be [[1, '1'], [2, '2']]
type exp = Zip<[], [true, false]> // expected to be []
实现
ts
type Zip<T extends unknown[], U extends unknown[]> = T extends [infer TF, ...infer TR] ? U extends [infer UF, ...infer UR] ? [[TF, UF], ...Zip<TR, UR>] : [] : []
// 也可以合并在一起判断
type Zip<T extends unknown[], U extends unknown[]> = [T, U] extends [[infer TF, ...infer TR], [infer UF, ...infer UR]] ? [[TF, UF], ...Zip<TR, UR>] : []
4、IsTuple(判断是否为元组)
ts
type case1 = IsTuple<[number]> // true
type case2 = IsTuple<readonly [number]> // true
type case3 = IsTuple<number[]> // false
实现
ts
type IsTuple<T> = [T] extends [never] ? false : T extends readonly any[] ? number extends T['length'] ? false : true : false
解释:
元组的length
是固定值,而any[]
的length是number
5、Chunk(切片)
ts
type exp1 = Chunk<[1, 2, 3], 2> // expected to be [[1, 2], [3]]
type exp2 = Chunk<[1, 2, 3], 4> // expected to be [[1, 2, 3]]
type exp3 = Chunk<[1, 2, 3], 1> // expected to be [[1], [2], [3]]
实现
ts
type Chunk<
T extends unknown[],
S extends number,
C extends unknown[] = [], // 存储当前
V extends unknown[] = [], // 存储结果
> = T extends [infer F, ...infer R]
? C['length'] extends S // 当前长度满足
? Chunk<R, S, [F], [...V, C]> // 将当前赋值给结果,重置当前
: Chunk<R, S, [...C, F], V> // 当前继续添加元素
: C extends [] // 结束时,判断当前是否有值
? V // 无值,直接返回结果
: [...V, C]; // 有值,添加到结果
简化一下:省一个参数
ts
type Chunk<
T extends unknown[],
S extends number = 1,
V extends unknown[] = [], // 存储当前
> = T extends [infer F, ...infer R]
? V['length'] extends S // 当前长度满足
? [V, ...Chunk<R, S, [F]>] // 将当前赋值给结果,并执行剩余
: Chunk<R, S, [...V, F]> // 当前继续添加元素
: V['length'] extends 0 // 结束时,判断当前是否有值
? V // 无值,直接返回[]
: [V]; // 有值,返回
6、Fill(填充)
ts
type exp = Fill<[1, 2, 3], 0> // expected to be [0, 0, 0]
type exp = Fill<[1, 2, 3], 0, 0, 0> // [1, 2, 3]
type exp = Fill<[1, 2, 3], true, 0, 1> // [true, 2, 3]
type exp = Fill<[1, 2, 3], true, 1, 3> // [1, true, true]
type exp = Fill<[1, 2, 3], true, 1, 2> // [1, true, 3]
第一种方式
ts
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T['length'],
R extends unknown[] = [],
> = T extends [infer First, ...infer Rest]
? R['length'] extends End // 如果到结束位置
? [...R, ...T] // 直接将前面已经替换的和剩余的合并返回
: R['length'] extends Start // 如果到了开始替换的位置
? Fill<Rest, N, [...R, N]['length'], End, [...R, N]> // 将开始位置加一,并存储替换的数据
: Fill<Rest, N, Start, End, [...R, First]> // 没到位置,就不加一,且保存不替换的数据
: R;
解释:
通过增加开始位置,判断继承关系来替换指定的值
第二种方式
ts
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T['length'],
C extends 0[] = [],
> = T extends [infer F, ...infer R]
? [
GreaterThan<Start, C['length']> extends true // 开始位置大于当前索引(当前索引未到开始位置)
? F // 不替换,使用原值
: GreaterThan<End, C['length']> extends true // 结束位置大于索引(还没结束)
? N // 需要替换
: F, // 结束了,不再替换
...Fill<R, N, Start, End, [...C, 0]>, // 剩余参数相同处理
]
: [];
解释:
不修改开始位置,采用大小判断,当前索引大于等于开始,小于结束才替换。
注意:此方法性能较差,用到了前面的GreaterThan工具
7、Trim Right(去除右侧空格)
ts
type Trimed = TrimRight<' Hello World '> // 应推导出 ' Hello World'
实现
ts
type TrimRight<S extends string> = S extends `${infer R}${' ' | '\t' | '\n'}` ? TrimRight<R> : S
8、Without(去除数组指定元素)
ts
type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []
实现
ts
type ToUnion<T> = T extends any[] ? T[number] : T
type Without<T extends unknown[], U> = T extends [infer F, ...infer R] ? F extends ToUnion<U> ? Without<R, U> : [F, ...Without<R, U>] : []
解释:
现将第二个类型转为联合类型,然后直接判断继承关系即可。
9、Trunc(取整)
ts
type A = Trunc<12.34> // 12
第一种方案(思路)
ts
type Trunc<T extends string | number> = `${T}` extends `-${infer R}`
? `-${Trunc<R>}` // 如果是负数,则添加负号,并对正数处理
: `${T}` extends `${infer F}.${any}` // 如果有小数点
? F extends '' // 判断整数位是否为空
? '0' // 为空返回0
: F // 不为空返回整数位
: `${T}`; // 不是小数,直接返回字符串
第二种方案(第一种简化)
ts
type Trunc<T extends string | number> = `${T}` extends `${infer N}.${any}` ? N extends '' | '-' | '+' ? `${N}0` : N : `${T}`;
第三种方案(填充0)
ts
type FillZero<T extends string | number> = `${T}` extends `${infer N extends '' | '-' | '+'}.${infer R}` ? `${N}0.${R}` : `${T}`;
type Trunc<T extends string | number> = FillZero<T> extends `${infer N}.${any}` ? N : `${T}`;
10、IndexOf(获取索引)
ts
type Res = IndexOf<[1, 2, 3], 2>; // expected to be 1
type Res1 = IndexOf<[2,6, 3,8,4,1,7, 3,9], 3>; // expected to be 2
type Res2 = IndexOf<[0, 0, 0], 2>; // expected to be -1
实现
ts
type IsEqual<T, S> = (<T>() => T extends S ? 1 : 0) extends (<S>() => S extends T ? 1 : 0) ? true : false;
type IndefOf<T extends unknown[], S, V extends 0[] = []> = S extends T[number]
? T extends [infer F, ...infer R]
? Equal<F, S> extends true
? V['length']
: IndefOf<R, S, [...V, 0]>
: -1
: -1;
解释:
1、先通过S extends T[number]
判断元组内是否有S,没有直接返回-1,不用寻找索引了
2、添加一个参数存储下标,找到时,返回下标,没找到时索引加一,再对剩余的处理
评论
0条评论
暂无内容,去看看其他的吧~