# <!-- --- title: TS类型体操 之 循环的几种方式 date: 2022-07-12 sidebar: auto categories:

# tags:

meta:

  • name: description content:
  • name: keywords content: --- -->

# TS 类型体操 之 循环的几种方式

程序中用好循环能节省很多重复的工作量,TS 也不例外。TS 循环的方式也有很多,最常见的就是 递归调用in 关键字数组的 [number]union 的自动解构

递归调用 in 循环应该都用的很多了,这里就不多说了。

in 需要注意的就是多数都需要在生成 {} 的时候才用得上。经典的 in 循环例子

type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}
1
2
3

在写第篇入门文章的时候提到还有几道题目没解出来。这里就占了很大的比例,非常有代表性的题目 3326・BEM style string 296・Permutation 4260・AllCombinations

# 3326 · BEM style string number 循环和递归调用的结合

先看需求:

type cases = [
  Expect<Equal<BEM1<'btn', ['price'], []>, 'btn__price'>>,
  Expect<Equal<BEM1<'btn', ['price'], ['warning', 'success']>, 'btn__price--warning' | 'btn__price--success'>>,
  Expect<Equal<BEM1<'btn', [], ['small', 'medium', 'large']>, 'btn--small' | 'btn--medium' | 'btn--large'>>
]
1
2
3
4
5

这是一个组合类的题目,需要把 3 个位置 依次拼接 可能出现的所有的情况组合起来。而且第二位与第一位的连接符是 __,第三位与前面的连接符是 --

解题的思路:

  1. 找到结束/输出答案的条件 在这里明显就是 第 3 个参数的数组为空(已经遍历完了)的时候所有的情况就列完了
  2. 因为每个地方连接符不一样,所以还得判断最一开始数组是否为空
  3. 需要用到数组的一个特性 T[number]

先看下 T[number] 的作用

// 🌰 - 1
type ArrayToUnion<T extends unknown[]> = T[number]
type ArrayUnion = ArrayToUnion<['1', '2', '3', '4']> // "1" | "2" | "3" | "4"

// 🌰 - 2
type TestUnion<T extends string, U extends string> = `${T}--${U}`
type TestUnionDemo = TestUnion<'1' | '2', '3' | '4'> // "1--3" | "1--4" | "2--3" | "2--4"

// 🌰 - 3
type TestArray<T extends string[], U extends string[]> = `${T[number]}--${U[number]}`
type TestArrayDemo = TestArray<['1', '2'], ['3', '4']> // "1--3" | "1--4" | "2--3" | "2--4"

1
2
3
4
5
6
7
8
9
10
11
12
  • 看例子 1 因为 T 是数组类型,[] 中传入就是数字下标,所以用 T[number] TS 会帮我们自动把下标都遍历一次,加上 TS 本身返回值的特性(如果返回了多个结果)会自动合并为 union 类型

  • 例子 2 之前讲 《TS 类型体操 之 extends,Equal,Alike 使用场景和实现对比》 的时候稍微提到过 union 类型会自动的 “解构”(也就是在原地替换多种情况并且作为结果之一返回)。所以例子 2 中可以看到 4 个 返回结果,就是他们两两相互结合的结果

  • 例子 3 结合了 T[number] 会自动转union 的特性 + union 自动解构循环的特性,得出的结果

理解了 3 个例子,BEM style 这个题目就好办了

解法 1:

  • 如果 2 个数组都为空,直接返回B
  • 剩下的就分别判断 2 个数组时候为空,分别就拼上对应的 __ 或者 -- 就行了
type BEM<B extends string, E extends string[], M extends string[]> = E extends []
  ? M extends []
    ? B
    : `${B}--${M[number]}`
  : M extends []
  ? `${B}__${E[number]}`
  : `${B}__${E[number]}--${M[number]}`
1
2
3
4
5
6
7

解法 2:

其实思路是一样的,只是把一些判断简化了一下,在解法 1 中,M extends [] 是出现了 2 次,其实可以利用递归的思想把他们合并到一起

重点就在于递归调用的时候,B 参数变成了 ${B}__${E[number]},这个留给你们自己细细琢磨

type BEM1<B extends string, E extends string[], M extends string[]> =
  E extends [] ? (M extends [] ? B : `${B}--${M[number]}`) : BEM1<`${B}__${E[number]}`, [], M>

1
2
3

union 顺便把 bem 的几道题目讲了 permutation

combination bem

T[number]

Last Updated: 7/15/2022, 6:40:36 PM