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

183 阅读3分钟

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

1. 引言

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

2. 题型

  1. 斐波那契序列:实现通用的Fibonacci取一个数字T并返回它对应的斐波那契数列。

序列开始:1,1,2,3,5,8,13,21,34,55,89,144,…

  type Result1 = Fibonacci<3> // 2
  type Result2 = Fibonacci<8> // 21

思路:   

首先,我们需要记录当前递归的次数,用来结束循环,这里用 N 来表示,数组里面的值给什么都可以,我们只是用来计数

type Fibonacci<T extends number, N extends number[] = [1]> = N['length'] extends T ? ( // 返回结果 ) : Fibonacci<T, [...N, 1]>

现在递归结束条件我们已经处理好了,那么需要开始计算了,我们需要将前面的结果加上当前的值,因此需要引入两个数,一个记录结果 Res ,一个记录当前值 Cur,关键在于这段代码

Fibonacci<T,[...N, 1], Cur, [...Res, ...Cur]>

把前面结果 Res 数组和 当前的 Cur 展开在一起,作为新的 Cur 进行递归,而当前的结果也就是 Cur 数组,通过数组展开的方式,把每个值都通过项数堆在一起,最后返回 length 即可 解答:

// your answers
type Fibonacci<
  T extends number, 
  R extends any[] = [], 
  U1 extends any[] = [], 
  U2 extends any[] = []
> =  
  R['length'] extends T 
    ? [...U1, ...U2]['length'] 
    : R['length'] extends (0 | 1) 
      ? Fibonacci<T, [...R, any], [], [any]> 
      : Fibonacci<T, [...R, any], U2, [...U1, ...U2]>

  1. AllCombinations: 实现类型返回最多一次使用s中的字符的所有字符串组合。
type AllCombinations_ABC = AllCombinations<'ABC'>;
// should be '' | 'A' | 'B' | 'C' | 'AB' | 'AC' | 'BA' | 'BC' | 'CA' | 'CB' | 'ABC' | 'ACB' | 'BAC' | 'BCA' | 'CAB' | 'CBA'

思路:    首先我们需要把字符串 S 转换成联合类型,这样我们就可以遍历它,再结合上对象转联合类型时的特征实现

首先我们需要实现一个字符串转 Union 的方法
递归字符串即可
type StrToUnion<S> = S extends `${infer R}${infer U}` ? R | StrToUnion<U> : never
利用对象转联合
我们先看看一个对象转成联合类型是什么样子的

会将 value 通过 | 连接

type ObjToUnion = { [P in keyof O]: O[P] }[keyof O]

type B = ObjToUnion<{'a': 1, 'b':2, 'c': 3}> // type B = 1 | 2 | 3

那么我们就可以利用这个特性来处理,也就是这样,我们通过递归的方式,把 value 进行排列

{
  [K in U]: `${K}${AllCombinations<never, Exclude<U, K>>}`
}[U]

但是这样得到的是字母间的全排列,我们还需要单个字符,因此需要在递归的时候加上 '' | 即可

因为每次递归时都会经历 ''、'A'、'AB'、'ABC' 这样逐渐累加字符的过程,而每次都会遇到 '' | 使其自然形成了联合类型

推演:

当输入 ABC 时,会通过 StrToUnion 转成 Union 类型
判断是不是 never ,因为递归过程中可能会有 never 出现
[K in U] 取类型中的一个,如 A, 递归 Exclude<U,K>,也就是 B,C,这样就从 ABC 到了 BC 接下来又到 C 所有字符都会被考虑

解答:


type AllCombinations<S extends string, U extends string = StrToUnion<S>> =
  [U] extends [never]
  ? ''
  : '' | {
    [K in U]: `${K}${AllCombinations<never, Exclude<U, K>>}`
  }[U]

  1. Greater Than: 在这个挑战中,你应该实现一个类型GreaterThan<T, U>像T比;U

负数不需要考虑。

GreaterThan<2, 1> //should be true
GreaterThan<1, 1> //should be false
GreaterThan<10, 100> //should be false
GreaterThan<111, 11> //should be true

思路:    通过引入新的变量 R extends any[] = [] ,来进行辅助的计算,接着依次判断 T,U 和 R['length] 是否相等,这时候,如果 T 和 R['length] 相等了而 U 还没有相等,那就说明了 T < U ,如果都不相等,那就继续加大数组 R 的长度;

递归的时候,往数组中多加一个任意值即可,GreaterThan<T, U, [...R, 1]> 这里的 1 就是塞进去把 length 整大的目的,塞入的值可以是任意值。 解答:


type GreaterThan<T extends number, U extends number, R extends any[] = []> = 
  T extends R['length']
    ? false
    : U extends R['length']
      ? true
      : GreaterThan<T, U, [...R, 1]>