Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type-challenges-solutions/en/medium-flatten #314

Open
utterances-bot opened this issue Dec 29, 2022 · 5 comments
Open

type-challenges-solutions/en/medium-flatten #314

utterances-bot opened this issue Dec 29, 2022 · 5 comments

Comments

@utterances-bot
Copy link

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

Copy link

MajorLift commented Dec 29, 2022

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>]
    : []

Copy link

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>]
  : [];

@MajorLift
Copy link

@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.

Copy link

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

Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants