【Typescript 系列】类型体操之中等篇题型(第十五节)解读

112 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

1. 引言

接着上一节中,接下来我们继续Ts中等篇的题型练习 https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!

2. 题型

  1. Reverse: 实现类型版本的数组反转 Array.reverse
type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']

思路:    使用infer 关键字拆解对应元组元素,在使用递归方法循环操作获得反转元组; 解答:

type Reverse<T> = T extends [infer M,...infer N] ? [...Reverse<N>,M] : []
type Demo = Reverse<[]> // type Demo = []
type Demo1 = Reverse<['a', 'b']> // type Demo1 = ["b", "a"]
type Demo2 = Reverse<['a', 'b', 'c']> // type Demo2 = ["c", "b", "a"]

  1. Flip Arguments:实现lodash的_.flip的类型版本。

类型FlipArguments需要函数类型T,并返回一个新的函数类型,其返回类型与T相同,但参数相反。

type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void> 
// (arg0: boolean, arg1: number, arg2: string) => void

思路:    先定义一个reverse方法,该方法作用可以使元祖内部的元素倒转,此方法通过infer关键字结构配合递归本身函数,获得一个倒转的元祖,在通过js函数中的argument 代指传入数据值的数组集合,配合reverse把元祖内部元素倒转,配合输出倒转后的元祖函数,完成;

解答:

// your answers
type Reverse<T> = T extends [infer Head, ...infer Rest] ? [...Reverse<Rest>, Head] : T
type FlipArguments<T extends Function> = T extends (...args: infer A) => infer R ? (...args: Reverse<A>) => R : T

  1. FlattenDepth:递归地将数组平展到深度时间。
type a = FlattenDepth<[1, 2, [3, 4], [[[5]]]], 2> // [1, 2, 3, 4, [5]]. flattern 2 times
type b = FlattenDepth<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, [[5]]]. Depth defaults to be 1

思路:    首先,①定义一个一次结构一层数组元素的FlattenOnce方法,该方法中,通过使用[]配合infer关键字,结构出每次要结构的元素和它以外的其他元素(等待结构),依次各项元素判断通过递归该函数结构;②定义一个IsFlatten 判断当前数祖各项是否为数祖的方法,还存在数组则返回false,否则代表数组已经“干净”,则返回true。 ③定义FlattenDepth方法,该方法需要传入三个参数:第一个参数代表判断对象,第二个需要结构的层数,第三个记录当前已经结构层数;先使用IsFlatten 判断当前初始对象本身是否已经是平铺到最底层的对象;如果是则无需判断直接回抛目标对象;如果不是判断当前的递归记录数组长度(等同于递归次数)是否已经满足第二参数所要求的平铺层数,如果是则回抛目标对象;如果以上都不是则执行FlattenDepth 递归函数本身,内部第一参数执行平铺一次数组的FlattenOnce 函数,第二参数保持不变传入题目要求的平铺数目;第三参数则利用递归函数的特性,传入一个记录当前递归函数次数的数组(没次递归新增传入数组一个any 字符用于拓展数组长度,为递归后的下次函数执行做铺垫);完成。 解答:

// 对 T 数组进行一次平铺
type FlattenOnce<T extends any[]> = T extends [infer F, ...infer R]
  ? [...(F extends any[] ? F : [F]), ...FlattenOnce<R>]
  : []

// T 是否平铺的数组
type IsFlatten<T extends any[]> = T extends [infer F, ...infer R]
  ? (
    F extends any[]
    ? false
    : IsFlatten<R>
  )
  : true

type FlattenDepth<T extends any[], D extends number = 1, Reduce extends any[] = []> =
  IsFlatten<T> extends true
  ? T
  : (
    Reduce['length'] extends D
    ? T
    : FlattenDepth<FlattenOnce<T>, D, [any, ...Reduce]>
  )