Skip to content

遍历

在 Typescript 类型中,通常用 extendsinfer 提取元素,用递归实现逐个提取。

我们可以通过内置工具类型 ReturnType 理解这种类型提取

ts
type ReturnType<T> = T extends (...args: any) => infer R ? R : any;

infer 表示从这个位置推导出类型,并赋给 R 参数,也就是提取类型。

示例格式

假定你需要遍历一个类型,并对其每个元素进行操作,就像 Array.prototype.map 一样。

ts
/**@template T 可遍历类型 */
type Each<T> = ...
/**@template T 元素类型 */
type Action<T> = ...

字符串

ts
type Each<T, S> = T extends `${infer L}${infer R}` ? Each<R, Action<L>> : S;

字符串可以通过 `${infer L}${infer R}` 提取第一个字符 L,而 R 表示剩下的字符串。

  • 如果 T 为空字符,则判断为假,直接返回 S
  • 如果 T 为单字符,则 R 为空字符

元组

ts
type Each<T, S> = T extends [infer L, ...infer R] ? Each<R, Action<L>> : S;

元组遍历比字符串的更灵活,也可以从尾部开始遍历。

联合类型

ts
type Each<T> = T extends any ? Action<T> : never;

联合类型可以直接使用 extends 遍历,但这只在类型参数上有效:

ts
type Arg = 0 | 1;
type Fn = Arg extends any ? (e: Arg) => void : never; //(e: 0 | 1) => void

在模板字符串中,联合类型将被自动遍历:

ts
type T = `${0 | 1}`; //'0' | '1'

补充

有时我们还需要逐个提取联合类型的元素,可以看看 UnionToTuple 是怎么实现的。

类型转换

UnionToIntersection

ts
/**联合类型转交叉类型 */
type UnionToIntersection<T> = (T extends any ? (e: T) => void : never) extends (e: infer U) => void
    ? U
    : never;

UnionToTuple

ts
/**获取联合类型最后一个元素 */
type Last<T> = UnionToIntersection<T extends any ? (e: T) => void : never> extends (
    e: infer U,
) => void
    ? U
    : never;
/**联合类型转元组 */
type UnionToTuple<T, S extends any[] = []> = Last<T> extends infer U
    ? U extends never
        ? S
        : UnionToTuple<Exclude<T, U>, [U, ...S]>
    : never;