type-challenges-solutions icon indicating copy to clipboard operation
type-challenges-solutions copied to clipboard

type-challenges-solutions/en/medium-flatten

Open utterances-bot opened this issue 2 years ago • 5 comments

Flatten

This project is aimed at helping you better understand how the type system works, writing your own utilities, or just having fun with the challenges.

https://ghaiklor.github.io/type-challenges-solutions/en/medium-flatten.html

utterances-bot avatar Dec 29 '22 05:12 utterances-bot

There are two issues with this solution:

Given the following test case...

// @ts-expect-error
type error = Flatten<undefined>
  • The T extends [] check is redundant. T is an empty array iff. T is an array and T extends [infer H, ...infer R] is false.
type Flatten<T extends unknown[]> = 
  T extends [infer H, ...infer Rest]
    ? H extends unknown[]
      ? [...Flatten<H>, ...Flatten<Rest>]
      : [H, ...Flatten<Rest>]
    : []
  • The solution uses T ambiguously -- for both the input array type and the inferred "tail" array type. Here's a slightly different formulation that relies on those two types being defined independently.
type Flatten<T extends any[]> = // T[0] cannot extend unknown
  T extends [infer H, ...infer Rest]
    ? H extends unknown[]
      ? [...Flatten<T[0]>, ...Flatten<Rest>]
      : [T[0], ...Flatten<Rest>]
    : []

MajorLift avatar Dec 29 '22 05:12 MajorLift

My solution is similar with @MajorLift one, except I'm using Flattern 1 instantiation less :P

type Flatten<T extends unknown[]> = T extends [infer Head, ...infer Tail]
  ? Head extends unknown[]
    ? Flatten<[...Head, ...Tail]>
    : [Head, ...Flatten<Tail>]
  : [];

albert-luta avatar Jan 29 '23 19:01 albert-luta

@albert-luta Nice! It seems that your solution would result in less optimal recursion depth, as the Flatten call will attempt to process the entire input array (with Head one level flattened) on a single stack. Depending on how deeply nested the elements of T are, this could result in a significant performance hit.

MajorLift avatar Jan 31 '23 04:01 MajorLift

I believe this initial example could be improved

type Flatten<T> = T extends []
  ? []
  : T extends [infer H, ...infer T]
  ? [H, T]
  : [T];
  • : T extends [infer H, ...infer T] -> : T extends [infer H, ...infer K] T could be replaced with K. Otherwise T after extends is "shadowing" the original T
  • ? [H, T] -> ? [H, ...K] ** T could be replaced with K as explained in the previous point ** There should be ... before K. Otherwise K is a tuple

okadurin avatar May 20 '23 08:05 okadurin

My solution using acc to trace the result while in recursion

  type Flatten<T extends unknown[], acc extends unknown[] = []> = T extends [infer F, ...infer R]
  ? F extends unknown[]
    ? Flatten<R, [...acc, ...Flatten<F, []>]>
    : Flatten<R, [...acc, F]>
  : acc

JanessaTech avatar Aug 16 '24 10:08 JanessaTech