# 32427-Unbox

# 题目描述

实现 Unbox<T, N?>,把数组 / 元组 / 函数返回值 / Promise 这些"盒子"层层剥开:

  • 基础:Unbox<T> 剥一层盒子(无盒子则原样返回)。
  • 进阶:默认情况下递归剥到底。
  • 再进阶:第二个参数 N 可控制剥的层数,N = 0 表示无限剥,N >= 1 表示剥 N 层。
type A = Unbox<number[]>; // number
type B = Unbox<() => Promise<() => number>>; // number
type C = Unbox<() => () => () => number, 2>; // () => number
type D = Unbox<Promise<Promise<number>>, 0>; // number  (0 代表剥到底)

# 分析

分两步:

  1. 实现 UnboxOne<T>:根据 T 的形态判断它是不是数组 / 元组 / 函数 / Promise,剥一层。
  2. 套个带计数器的递归:
    • 如果 N = 0,一直剥到盒子里不再是盒子(即 UnboxOne<T> = T)为止;
    • 如果 N >= 1,计数累到 N 就停,不管底层还有没有盒子;
    • 途中如果已经剥到"非盒子",也直接结束。

匹配数组、元组、函数、Promise 的 infer

  • 数组和元组:T extends (infer U)[] ? U 可以同时命中 number[][number](元组也 extends any[])。
  • 函数:T extends () => infer U ? U
  • Promise:T extends Promise<infer U> ? U

注意匹配顺序:先匹 Promise、再匹数组/元组、最后匹函数——因为 Promise 的内部并不是函数形态,但数组和函数互斥,顺序基本无所谓。

# 题解

type UnboxOne<T> = T extends Promise<infer U>
  ? U
  : T extends (infer U)[]
  ? U
  : T extends () => infer U
  ? U
  : T;

type Unbox<T, N extends number = 0, C extends any[] = []> = N extends 0
  ? // 无限剥:剥到底就停
    UnboxOne<T> extends T
    ? T
    : Unbox<UnboxOne<T>, 0>
  : // 计数剥:剥到 N 层或剥不动就停
  C['length'] extends N
  ? T
  : UnboxOne<T> extends T
  ? T
  : Unbox<UnboxOne<T>, N, [...C, 1]>;

解读:

  • UnboxOne<T>:只剥一层。若 T 不是任何盒子,直接返 T,所以 UnboxOne<UnboxOne<...>> = T 就意味着已经剥到头。
  • N = 0 分支:靠 UnboxOne<T> extends T 判断是不是"盒子里的东西等于盒子自身"——这是到底的信号,直接返回 T。否则把 UnboxOne<T> 继续往下递归。
  • N >= 1 分支:额外维护一个长度计数元组 C,每一层递归 C 增一个元素;C['length'] extends N 即剥满了。若中途已经剥到头(UnboxOne<T> extends T),也直接停。

# 验证

type cases = [
  Unbox<number>, // number
  Unbox<() => number>, // number
  Unbox<number[]>, // number
  Unbox<[number]>, // number
  Unbox<Promise<number>>, // number
  Unbox<() => Promise<() => Array<Promise<boolean>>>>, // boolean
  Unbox<() => () => () => () => number, 1>, // () => () => () => number
  Unbox<() => () => () => () => number, 4>, // number
  Unbox<Promise<Promise<Promise<number>>>, 2>, // Promise<number>
];

# 知识点

  • (infer U)[] 一次性覆盖 number[][number](元组是数组的子类型),是消除"数组 / 元组"类题目分支的捷径。
  • 递归 + "固定点检测"(UnboxOne<T> extends T 即剥不动了)是无限剥类题目的通用写法。
  • 用元组 C 做步数计数、C['length'] extends N 做出口,是类型层做"有限循环"的标准手段,见 进阶-计数-加减乘除
Last Updated: 2026/4/19 03:05:53