rimo030 / type-challenges

Collection of TypeScript type challenges with online judge
https://tsch.js.org/
MIT License
3 stars 0 forks source link

no - 32427 Unbox #109

Open rimo030 opened 5 months ago

rimo030 commented 5 months ago
type Unbox<T, C extends number = 0, I extends any[] = [0]> =  T extends (Promise<infer R>) | (() => infer R) |  (Array<infer R>) |  [infer R]
  ? I['length'] extends C 
    ? R 
    : Unbox<R, C, [...I, 0]>
  : T
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  // Base cases
  Expect<Equal<Unbox<number>, number>>,
  Expect<Equal<Unbox<() => number>, number>>,
  Expect<Equal<Unbox<() => number | string>, number | string>>,
  Expect<Equal<Unbox<number[]>, number>>,
  Expect<Equal<Unbox<(number | string)[]>, number | string>>,
  Expect<Equal<Unbox<[number]>, number>>,
  Expect<Equal<Unbox<Promise<number>>, number>>,

  // Bonus: Recursion
  Expect<Equal<Unbox<() => Promise<() => Array<Promise<boolean>>>>, boolean>>,

  // Bonus: Recusion levels
  Expect<Equal<Unbox<() => () => () => () => number, 0>, number>>,
  Expect<Equal<Unbox<() => () => () => () => number, 1>, () => () => () => number>>,
  Expect<Equal<Unbox<() => () => () => () => number, 2>, () => () => number>>,
  Expect<Equal<Unbox<() => () => () => () => number, 3>, () => number>>,
  Expect<Equal<Unbox<() => () => () => () => number, 4>, number>>,
  Expect<Equal<Unbox<() => () => () => () => number, 5>, number>>,
  Expect<Equal<Unbox<number[][][][], 0>, number>>,
  Expect<Equal<Unbox<number[][][][], 1>, number[][][]>>,
  Expect<Equal<Unbox<number[][][][], 2>, number[][]>>,
  Expect<Equal<Unbox<number[][][][], 3>, number[]>>,
  Expect<Equal<Unbox<number[][][][], 4>, number>>,
  Expect<Equal<Unbox<number[][][][], 5>, number>>,
  Expect<Equal<Unbox<[[[[number]]]], 0>, number>>,
  Expect<Equal<Unbox<[[[[number]]]], 1>, [[[number]]]>>,
  Expect<Equal<Unbox<[[[[number]]]], 2>, [[number]]>>,
  Expect<Equal<Unbox<[[[[number]]]], 3>, [number]>>,
  Expect<Equal<Unbox<[[[[number]]]], 4>, number>>,
  Expect<Equal<Unbox<[[[[number]]]], 5>, number>>,
  Expect<Equal<Unbox<Promise<Promise<Promise<number>>>, 0>, number>>,
  Expect<Equal<Unbox<Promise<Promise<Promise<number>>>, 1>, Promise<Promise<number>>>>,
  Expect<Equal<Unbox<Promise<Promise<Promise<number>>>, 2>, Promise<number>>>,
  Expect<Equal<Unbox<Promise<Promise<Promise<number>>>, 3>, number>>,
  Expect<Equal<Unbox<Promise<Promise<Promise<number>>>, 4>, number>>,
]
rimo030 commented 5 months ago

처음에는 아래와 같이 작성했었다.


type Unbox<T, C extends number = 0, I extends any[] = []> = C extends 0 
  ? Unbox<T, -1>
  : I['length'] extends C
    ? T
    : T extends Promise<infer R>
      ? Unbox<R, C, [...I, 0]>
      : T extends () => infer R
        ? Unbox<R, C, [...I, 0]>
        : T extends Array<infer R>
          ? Unbox<R, C, [...I, 0]>
          : T extends [infer R]
            ? Unbox<R, C, [...I, 0]>
            : T

배열 타입 I로 Depth를 계산하고, 입력받은 C에 도달했다면 재귀가 종료되며 T가 반환된다.

C가 0이라면 끝까지 전개해야 하므로, C에 도달 하지 않도록 한다.

나는 -1을 대입하였다.

rimo030 commented 5 months ago

반복되는 부분을 정리하면 다음과 같다.

type Unbox<T, C extends number = 0, I extends any[] = []> = C extends 0 
  ? Unbox<T, -1>
  : I['length'] extends C
    ? T
    : T extends Promise<infer R> | (() => infer R) | Array<infer R> | [infer R]
      ? Unbox<R, C, [...I, 0]>
      : T
rimo030 commented 5 months ago

더 줄일수는 없을까? 🤔

Unbox<T, 0>이 들어오면 최대한 전개해야 하고 Unbox<T, n> (n>0)오면 숫자 만큼 전개한다.

따라서 조건에 맞다면, 무조건 한 번 이상 전개 해야 한다.

그렇다면 Depth의 초기값을 1로 주고,

type Unbox<T, C extends number = 0, I extends any[] = [0]> = ...

depth에 도달하지 않았다면 전개한다에서 전개하고 depth에 도달했는지 확인한다.로 바꾸어 볼 수 있다.

type Unbox<T, C extends number = 0, I extends any[] = [0]> =  T extends (Promise<infer R>) | (() => infer R) |  (Array<infer R>) |  [infer R]
  ? I['length'] extends C 
    ? R 
    : Unbox<R, C, [...I, 0]>
  : T