TS试炼-9

摘要:

包含:Parse URL Params(解析URL参数)、获取数组的中间元素、找出目标数组中只出现过一次的元素、统计数组中的元素个数、整数、将类型为字面类型(标签类型)的属性,转换为基本类型、DeepMutable(深度可变)、All(元组每项与传入类型相同)、Filter(过滤)、FindAll(获取所有下标)

1、Parse URL Params(解析URL参数)

Github练习

ts 复制代码
ParseUrlParams<':id'> // id
ParseUrlParams<'posts/:id'> // id
ParseUrlParams<'posts/:id/:user'> // id | user

实现

ts 复制代码
type ParseUrlParams<T extends string> = T extends `${string}:${infer P}` // 通过:分割,拿到剩余参数
  ? P extends `${infer F}/${infer R}`  // 判断剩余参数后是否有/,有就根据/分割
    ? F | ParseUrlParams<R> // 有/就将/前的与剩余参数联合
    : P  // 没有/,直接返回:后的
  : never;

2、获取数组的中间元素

Github练习

ts 复制代码
type simple1 = GetMiddleElement<[1, 2, 3, 4, 5]>, // 返回 [3]
type simple2 = GetMiddleElement<[1, 2, 3, 4, 5, 6]> // 返回 [3, 4]

第一种方案

ts 复制代码
type GetMiddleElement<T extends unknown[]> = T['length'] extends 0 | 1 | 2 // 元组长度小于3时,都是返回自身
  ? T
  : T extends [any, ...infer C, any] // 长度大于等于3时,去除两边的,保留中间
    ? GetMiddleElement<C> // 对中间的参数递归,得到最终结果
    : never;

第二种方案

ts 复制代码
type GetMiddleElement<T extends any[]> = T extends [infer L, ...infer M, infer R] // 判断T最少有两个元素
  ? M extends []  // 判断是否只有两个元素
    ? [L, R] // 只有两个直接返回
    : GetMiddleElement<M> // 递归执行剩余项
  : T;  // 少于两个元素,直接返回

3、找出目标数组中只出现过一次的元素

Github练习

ts 复制代码
FindEles<[1, 2, 2, 3, 3, 4, 5, 6, 6, 6]> // [1, 4, 5]
FindEles<[2, 2, 3, 3, 6, 6, 6]> // []
FindEles<[1, 2, 3]> // [1, 2, 3]
FindEles<[number, 1, 2]> // [number, 1, 2]
FindEles<[1, 2, number, number]> // [1, 2]

实现

ts 复制代码
type IsExist<T, C> = T extends T ? (Equal<T, C> extends true ? true : false) : never;
type FindEles<T extends any[], U extends unknown[] = []> = T extends [infer F, ...infer R]
  ? IsExist<[...U, ...R][number], F> extends false // 判断当前是否在剩余参数及已存在的参数内
    ? [F, ...FindEles<R, U>] // 不在里面,则添加到结果
    : FindEles<R, [...U, F]> // 在里面,不添加
  : [];

4、统计数组中的元素个数

Github练习

ts 复制代码
type Simple1 = CountElementNumberToObject<[]> // return {}
type Simple2 = CountElementNumberToObject<[1,2,3,4,5]> // { 1: 1, 2: 1, 3: 1, 4:1 , 5: 1 }
type Simple3 = CountElementNumberToObject<[1,2,3,4,5,[1,2,3]]>  // { 1: 2, 2: 2, 3: 2, 4:1 , 5: 1 }

第一种方式

ts 复制代码
type CountElementNumberToObject<T extends unknown[], U extends Record<PropertyKey, 0[]> = {}> = T extends [infer F, ...infer R] // 判断是否结束
  ? [F] extends [never]  // 判断当前是否为never
    ? CountElementNumberToObject<R, U>  // 为never,则忽略,继续执行后续
    : F extends any[] // 如果当前是数组
      ? CountElementNumberToObject<[...F, ...R], U> // 将数组平铺,继续执行
      : CountElementNumberToObject<R, {   // 不是数组,准备统计
          [P in keyof U | F & PropertyKey]: P extends keyof U // 判断是否为新增属性
            ? (P extends F ? [...U[P], 0] : U[P])  // 不是新增属性,为对应项元组添加长度
              : [0];  // 新增属性,初始化元组
        }>
  : {
    [P in keyof U]: U[P]['length'];  // 结束,返回统计结果
  };

第二种方式

ts 复制代码
// 平铺数组
type Flat<T> = T extends [infer F, ...infer R] ? F extends any[] ? [...Flat<F>, ...Flat<R>] : [F, ...Flat<R>] : T;
// 统计每一项,在平铺数组中的数量
type Count<T, O, U extends 0[] = []> = T extends [infer F, ...infer R] ? F extends O
  ? Count<R, O, [...U, 0]>
  : Count<R, O, [...U]>
: U['length'];
// 返回统计结果
type CountElementNumberToObject<T extends any[], U extends any[] = Flat<T>> = {
  [K in U[number]]: Count<U, K>;
};

5、整数

Github练习

ts 复制代码
Integer<1> // 1
Integer<1.1> // never
Integer<1.0> // 1

实现

ts 复制代码
// 常规思路
type Integer<T extends number> = number extends T ? never : `${T}` extends `${string}.${string}` ? never : T
// 学习新知识:bigint
type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never

6、将类型为字面类型(标签类型)的属性,转换为基本类型

Github练习

ts 复制代码
// { name: string, age: number, married: boolean, addr: { home: string, phone: string } }
type PersonInfo = { name: 'Tom', age: 30, married: false, addr: { home: '123456', phone: '13111111111' } }

第一种方式(valueOf)

ts 复制代码
type ToPrimitive<T>
  = T extends Function ? Function
  : T extends object ? { [K in keyof T]: ToPrimitive<T[K]> }
  : T extends { valueOf(): infer R } ? R
  : T

第二种方式

ts 复制代码
type ToPrimitive<T> = {
  [P in keyof T]: T[P] extends string
    ? string
    : T[P] extends number
      ? number
      : T[P] extends boolean
        ? boolean
        : T[P] extends Function
          ? Function
          : T[P] extends object
            ? ToPrimitive<T[P]>
            : T[P];
};

7、DeepMutable(深度可变)

Github练习

ts 复制代码
DeepMutable<{ readonly a: string, readonly b: { readonly c: number }> // { a: string, b: { c: number }

实现

ts 复制代码
type DeepMutable<T extends object> = {
  -readonly [P in keyof T]: T[P] extends Function ? T[P] : T[P] extends object ? DeepMutable<T[P]> : T[P]
}
type DeepMutable<T extends object> = T extends Function ? T : {
  -readonly [P in keyof T]: T[P] extends object ? DeepMutable<T[P]> : T[P]
}

8、All(元组每项与传入类型相同)

Github练习

ts 复制代码
All<[1, 1, 1], 1> // 应与 true 相同
All<[1, 2, 1], 1> // 应与 false 相同

实现

ts 复制代码
type All<T extends unknown[], V> = T extends [infer F, ...infer R] ? Equal<F, V> extends true ? All<R, V> : false : true

9、Filter(过滤)

Github练习

ts 复制代码
Filter<[0, 1, 2], 2> // [2]
Filter<[0, 1, 2], 0 | 1> // [0, 1]
Filter<[0, 1, 2], false | 0 | '' | null | undefined> // [0]

实现

ts 复制代码
type Filter<T extends any[], P> = T extends [infer F, ...infer R] ? F extends P ? [F, ...Filter<R, P>] : Filter<R, P> : []

10、FindAll(获取所有下标)

Github练习

ts 复制代码
FindAll<'Collection of TypeScript type challenges', 'Type'> // [14]
FindAll<'Collection of TypeScript type challenges', 'pe'> // [16, 27]
FindAll<'Collection of TypeScript type challenges', ''> // []
FindAll<'', 'Type'> // []
FindAll<'', ''> // []
FindAll<'AAAA', 'A'> // [0, 1, 2, 3]
FindAll<'AAAA', 'AA'> // [0, 1, 2]

第一种方案

ts 复制代码
// 获取字符串的长度
type StringLength<
  T extends string,
  U extends 0[] = []
> = T extends `${string}${infer R}` ? StringLength<R, [...U, 0]> : U['length']
type FindAll<
  T extends string,  // 要匹配的字符串
  P extends string,  // 匹配的字符
  U extends string = '', // 已经匹配的字符
  R extends number[] = [] // 最终结果
> = P extends ''
  ? []  // 如果匹配的字段为空,直接返回
  : T extends `${U}${infer F}${P}${string}` // 判断字符串中是否还有符合的
  ? FindAll< // 有符合的,添加结果,执行后续
      T,  // 原字符串不变
      P,  // 匹配字符不变
      `${U}${F}${P extends `${infer PF}${string}` ? PF : P}`, // 已匹配的字符串组合
      [...R, StringLength<`${U}${F}`>]  // 结果添加
    >
  : R  // 没有符合的,直接返回结果

第二种方案

ts 复制代码
type FindAll<
  T extends string,  // 要匹配的字符串
  S extends string,  // 匹配的字符
  P extends 0[] = [],  // 已匹配的长度
  R extends number[] = [] // 结果
> = S extends ''
  ? [] // 如果匹配的字段为空,直接返回
  : T extends `${string}${infer L}` // 将要匹配的字符串去除第一个字符,方便后续使用
  ? T extends `${S}${string}` // 匹配到字符开头
    ? FindAll<L, S, [...P, 0], [...R, P['length']]> // 已匹配长度加一,现长度添加到结果,执行后续
    : FindAll<L, S, [...P, 0], R> // 已匹配长度加一,执行后续
  : R // 返回结果

// 简化版本
type FindAll<
  T extends string,
  P extends string,
  L extends 0[] = []
> = P extends ''
  ? []
  : T extends `${string}${infer R}`
  ? [
      ...(T extends `${P}${string}` ? [L['length']] : []),
      ...FindAll<R, P, [0, ...L]>
    ]
  : []

评论

0条评论

logo

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