# <!-- --- 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]
}
2
3
# 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'>>
]
2
3
4
5
这是一个组合类的题目,需要把 3 个位置 依次拼接 可能出现的所有的情况组合起来。而且第二位与第一位的连接符是 __
,第三位与前面的连接符是 --
解题的思路:
- 找到结束/输出答案的条件 在这里明显就是 第 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"
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]}`
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>
2
3
union 顺便把 bem 的几道题目讲了 permutation
combination bem
T[number]