TS试炼-8

摘要:

包含:Join(Array.join)、LastIndexOf(Array.lastIndexOf)、Unique(去重)、MapTypes(Map类型转换)、Construct Tuple(构造元组)、Number Range(数字范围联合)、Combination(组合)、Subsequence(子序列)、CheckRepeatedChars(判断字符串是否有相同字符)、FirstUniqueCharIndex(第一个不重复字符下标)

1、Join(Array.join)

Github练习

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)

Github练习

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(去重)

Github练习

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类型转换)

Github练习

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(构造元组)

Github练习

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(数字范围联合)

Github练习

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(组合)

Github练习

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(子序列)

Github练习

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(判断字符串是否有相同字符)

Github练习

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(第一个不重复字符下标)

Github练习

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条评论

logo

暂无内容,去看看其他的吧~