TS试炼-8
摘要:
包含:Join(Array.join)、LastIndexOf(Array.lastIndexOf)、Unique(去重)、MapTypes(Map类型转换)、Construct Tuple(构造元组)、Number Range(数字范围联合)、Combination(组合)、Subsequence(子序列)、CheckRepeatedChars(判断字符串是否有相同字符)、FirstUniqueCharIndex(第一个不重复字符下标)
1、Join(Array.join)
ts
type Res = Join<["a", "p", "p", "l", "e"], "-">; // expected to be 'a-p-p-l-e'
type Res1 = Join<["Hello", "World"], " ">; // expected to be 'Hello World'
type Res2 = Join<["2", "2", "2"], 1>; // expected to be '21212'
type Res3 = Join<["o"], "u">; // expected to be 'o'
实现
ts
type Join<T extends (string | number)[], U extends string | number = ','> = T extends [
infer F extends string,
...infer R extends string[],
]
? `${F}${R['length'] extends 0 ? '' : U}${Join<R, U>}`
: '';
解释:
主要判断剩余的元组是否有值,有值才拼接,没有值就拼接空字符串
2、LastIndexOf(Array.lastIndexOf)
ts
type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1
实现
ts
type IsEqual<T, S> = (<T>() => T extends S ? 1 : 0) extends (<S>() => S extends T ? 1 : 0) ? true : false;
type LastIndexOf<T extends unknown[], S> = S extends T[number]
? T extends [...infer R, infer L]
? IsEqual<L, S> extends true
? R['length']
: LastIndexOf<R, S>
: -1
: -1;
解释:
1、先通过S extends T[number]判断元组内是否有S,没有直接返回-1,不用寻找索引了
2、从后面一个一个比对,找到时,返回剩余元组长度。和IndexOf类似
3、Unique(去重)
ts
type Res = Unique<[1, 1, 2, 2, 3, 3]>; // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]>; // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, "a", 2, "b", 2, "a"]>; // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, "a", 1, string, 2, "b", 2, number]>; // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]>; // expected to be [unknown, any, never]
第一种方式
ts
type Includes<T extends unknown[], V> = T extends [infer F, ...infer R] ? Equal<F, V> extends true ? true : Includes<R, V> : false
type Unique<T extends unknown[], V extends unknown[] = []> = T extends [infer F, ...infer R] ? Includes<V, F> extends true ? Unique<R, V> : Unique<R, [...V, F]> : V
解析
嵌套循环,跟常规js代码一样,原数据里的每一项跟新数据每一项比对
第二种方式
ts
type Unique<T, U = never> =
T extends [infer F, ...infer R]
? true extends (U extends U ? Equal<U, [F]> : never)
? Unique<R, U>
: [F, ...Unique<R, U | [F]>]
: []
解释
1、U作为一个联合类型来用
2、通过联合类型的遍历特性,判断U里的每一项是否有相同的:(U extends U ? Equal<U, [F]> : never)
此处,如果U里有和F相同的,返回boolean或true,只有完全没有相同的才会返回false
3、通过true extends xxx
来判断U里是否已经有F了,有就不添加,执行后续,没有就添加,然后执行后续。
4、MapTypes(Map类型转换)
ts
Expect<Equal<MapTypes<{ stringToArray: string }, { mapFrom: string, mapTo: [] }>, { stringToArray: [] }>>,
Expect<Equal<MapTypes<{ stringToNumber: string }, { mapFrom: string, mapTo: number }>, { stringToNumber: number }>>,
Expect<Equal<MapTypes<{ stringToNumber: string, skipParsingMe: boolean }, { mapFrom: string, mapTo: number }>, { stringToNumber: number, skipParsingMe: boolean }>>,
Expect<Equal<MapTypes<{ date: string }, { mapFrom: string, mapTo: Date } | { mapFrom: string, mapTo: null }>, { date: null | Date }>>,
Expect<Equal<MapTypes<{ date: string }, { mapFrom: string, mapTo: Date | null }>, { date: null | Date }>>,
Expect<Equal<MapTypes<{ fields: Record<string, boolean> }, { mapFrom: Record<string, boolean>, mapTo: string[] }>, { fields: string[] }>>,
Expect<Equal<MapTypes<{ name: string }, { mapFrom: boolean, mapTo: never }>, { name: string }>>,
Expect<Equal<MapTypes<{ name: string, date: Date }, { mapFrom: string, mapTo: boolean } | { mapFrom: Date, mapTo: string }>, { name: boolean, date: string }>>,
第一种方案
ts
type MapTypes<T extends Record<string, any>, R extends { mapFrom: any; mapTo: any }> = {
[P in keyof T]: T[P] extends R['mapFrom'] ? R extends R ? (R['mapFrom'] extends T[P] ? R['mapTo'] : never) : never : T[P];
};
解释:
1、判断当前属性类型是否在mapForm的联合类型中,不在就不替换
2、在联合类型中,就对每一个进行判断,符合就替换,不符合使用never不替换
第二种方案
ts
type MapTypes<T, R extends { mapFrom: any; mapTo: any }> = {
[K in keyof T]: T[K] extends R['mapFrom'] ? R extends { mapFrom: T[K] } ? R['mapTo'] : never : T[K]
}
解析
跟第一种差不多,将第二步的判断合并了。
5、Construct Tuple(构造元组)
ts
type result = ConstructTuple<2> // 期望得到 [unknown, unkonwn]
实现
ts
type ConstructTuple<L extends number, V extends unknown[] = []> = V['length'] extends L ? V : ConstructTuple<L, [...V, unknown]>
注意:此方法仅能支持1000以内,不然会超过递归次数
更大数支持
ts
type ConstructTupleUnit<T extends number | string, U extends any[] = []> = `${U['length']}` extends `${T}` ? U : ConstructTupleUnit<T, [unknown, ...U]>
type X10<T extends unknown[] = []> = [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T]
type ConstructTuple<T extends number | string, U extends unknown[] = []> = `${T}` extends `${infer L}${infer R}`
? ConstructTuple<R, [...ConstructTupleUnit<L>, ...X10<U>]>
: U
解析:
1、将基础实现作为一个基础,仅用于个位数
2、添加一个x10的类型,用于对某个元组乘10
3、对每一位数填充,并将前面位数填充的乘10
4、这样此类型将不受递归数量限制,而受元组长度限制,可以支持到最大元组(可以到10000以内)
6、Number Range(数字范围联合)
ts
type result = NumberRange<2 , 9> // | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
第一种方式
ts
type NumberRange<
L extends number,
H extends number,
C extends unknown[] = [], // 充当结果及索引
> = C['length'] extends H // 到结束位置
? [...C, H][number] 包含结束,返回联合
: C['length'] extends L // 到开始位置了
? NumberRange<[...C, L]['length'], H, [...C, L]> // 添加当前到结果,并把开始位置+1
: NumberRange<L, H, [...C, never]>; // 还没开始,开始结束位置不变,结果填充never
第二种方式
ts
type Utils<L, C extends 0[] = [], R = L> = C['length'] extends L ? R : Utils<L, [...C, 0], C['length'] | R>
type NumberRange<L, H> = L | Exclude<Utils<H>, Utils<L>>
解释
1、实现获取从0到指定数的联合类型Utils
2、将从0到最大数的联合,排除从0到最小数的联合,在联合上最小数
3、以2-4为例:2 | Exclude<0 | 1 | 2 | 3 | 4, 0 | 1 | 2> -> 2 | 3 | 4
7、Combination(组合)
ts
// expected to be `"foo" | "bar" | "baz" | "foo bar" | "foo bar baz" | "foo baz" | "foo baz bar" | "bar foo" | "bar foo baz" | "bar baz" | "bar baz foo" | "baz foo" | "baz foo bar" | "baz bar" | "baz bar foo"`
type Keys = Combination<['foo', 'bar', 'baz']>
第一种方式
ts
type Combination<
T extends string[],
V extends string = T[number],
Z extends string = V,
> = V extends V ? `${V}${'' | ` ${Combination<[], Exclude<Z, V>>}`}` : never;
解析:
拼接时,需要联合空字符串,这样才能把非全部的联合出来,分隔符要加在剩余参数的结果里
第二种方式
ts
type Combination<
T extends string[],
A = T[number],
U = A
> = U extends infer I extends string ? I | `${I} ${Combination<[], Exclude<A, I>>}` :never
解析
和第一种相同,只是没使用空字符串,相当于提出来了。
8、Subsequence(子序列)
ts
type A = Subsequence<[1, 2]> // [] | [1] | [2] | [1, 2]
第一种方式
ts
type Subsequence<T extends any[]> = T extends [infer F, ...infer R] ? [F, ...Subsequence<R>] | Subsequence<R> : [];
第二种方式
ts
type Subsequence<T extends any[],V extends any[] = []> = T extends [infer F, ...infer R] ? Subsequence<R, V | [...V, F]> : V
9、CheckRepeatedChars(判断字符串是否有相同字符)
ts
type CheckRepeatedChars<'abc'> // false
type CheckRepeatedChars<'aba'> // true
第一种方式
ts
type CheckRepeatedChars<
T extends string,
C extends string[] = [], // 定义一个元组存储已经存在的字符
> = T extends `${infer F}${infer R}`
? F extends C[number] // 第一个字符在元组中已经存在,返回
? true
: CheckRepeatedChars<R, [...C, F]> // 继续判断后续字符
: false;
type CheckRepeatedChars<
T extends string,
C = never, // 定义一个联合类型存储已经存在的字符
> = T extends `${infer F}${infer R}`
? F extends C // 第一个字符在联合类型中已经存在,返回
? true
: CheckRepeatedChars<R, C | F> // 继续判断后续字符
: false;
第二种方式
ts
type CheckRepeatedChars<T extends string> = T extends `${infer F}${infer E}`
? E extends `${string}${F}${string}` // 如果剩余字符内有前一个字符,则说明重复,返回
? true
: CheckRepeatedChars<E> // 继续判断后续字符
: false
10、FirstUniqueCharIndex(第一个不重复字符下标)
ts
type FirstUniqueCharIndex<'leetcode'> // 0
type FirstUniqueCharIndex<'loveleetcode'> // 2
type FirstUniqueCharIndex<'aabb'> // -1
第一种方式
ts
type StringToUnion<T extends string> = T extends `${infer F}${infer R}` ? F | StringToUnion<R> : never
type FirstUniqueCharIndex<
T extends string,
C extends string[] = [],
> = T extends `${infer F}${infer R}`
? F extends StringToUnion<R> | C[number] // 判断当前字符在剩余字符和已经存在的字符的联合类型中是否存在
? FirstUniqueCharIndex<R, [...C, F]> // 如果存在,说明有相同,继续执行后续
: C['length'] // 不存在,说明没有相同的,返回已判断的长度
: -1; // 找不到不同的,返回-1
第二种方式
ts
type FirstUniqueCharIndex<
T extends string,
N extends string = T,
R extends unknown[] = [],
> = T extends ''
? -1 // 如果是空字符串,直接返回-1
: N extends `${infer F1}${infer S}`
? T extends `${string}${F1}${string}${F1}${string}` // 如果在原字符串中找到两个当前判断的字符
? FirstUniqueCharIndex<T, S, [...R, unknown]> // 说明当前字符有相同的,将原字符串,剩余字符串,和当前已判断的长度,作为参数执行下一次
: R['length'] // 如果不符合,说明不存在两个,直接返回长度
: -1;
评论
0条评论
暂无内容,去看看其他的吧~