DEV Community

Pragmatic Maciej
Pragmatic Maciej

Posted on

7 2

Advanced TypeScript Exercises - Answer 6

6.1 Naive version (lower difficulty)

type NaiveFlat<T extends any[]> = {
  [K in keyof T]: T[K] extends any[] ? T[K][number] : T[K]
}[number];
Enter fullscreen mode Exit fullscreen mode

The solution of naive version is mapped type which traverse through type T given as argument. Important parts:

  • T[K] extends any[] - question if value type of property K is an array
  • T[K] extends any[] ? T[K][number] : T[K] - if T[K] is an array we get its value type by indexed type [number]. number is correct key of T[K] as T[K] was checked before for being an array, and array has number keys
  • [number] at the end has a purpose of getting all value types of produced type

6.2 Deep version (higher difficulty)

type DeepFlat<T extends any[]> = {
  [K in keyof T]: T[K] extends any[] ? DeepFlat<T[K]> : T[K]
}[number]
Enter fullscreen mode Exit fullscreen mode

The solution of deep version is different only in one point, instead of T[K][number] we go further and call DeepFlat<T[K]> so its recursive type, it calls itself. Thanks to the recursive nature we traverse the object until value type is not an array what is visible in "else" part : T[K]

The full code with test suite can be found in The Playground

This series will continue. If you want to know about new exciting questions from advanced TypeScript please follow me on dev.to and twitter.

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (6)

Collapse
 
skoloney profile image
Sergey Koloney •

Really liked this one, thanks!

Accidentally built DeepFlat, while trying to build a naive one lol

type DeepFlat<T extends any[]> =
  T extends [head: infer A, ...tail: infer B] ?
    (A extends any[] ? DeepFlat<A> : A) | DeepFlat<B> : never
Enter fullscreen mode Exit fullscreen mode
Collapse
 
regevbr profile image
Regev Brody •

Here is another solution that utilizes array desctructing:

type DeepFlat<T extends any[]> = 
  T extends [infer Head, ...infer Tail]
    ? Head extends any[]
      ? [...DeepFlat<Head>, ...DeepFlat<Tail>]
      : [Head, ...DeepFlat<Tail>]
    : T;
// test case
type Deep = [['a'], ['b', 'c'], [['d']], [[[['e']]]],'g'];
type DeepTestResult = DeepFlat<Deep>  
// should evaluate to "a" | "b" | "c" | "d" | "e" | 'g'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jopie64 profile image
Johan • • Edited

So I tried in TypeScript Playground and the following seems also a valid solution for the naive problem:


type NaiveFlat<T extends any[]> = T[number] extends any[] ? T[number][number] : T[number]

But this approach doesn't seem to work for DeepFlat


type DeepFlat<T extends any[]> = T[number] extends any[] ? DeepFlat<T[number]> : T[number]

It screems about that it circularly references itself. But doesn't it do that in your approach too? Why is it not an error in your case?

Collapse
 
macsikora profile image
Pragmatic Maciej •

Hi, thanks for the comment.
In order to avoid this message we can make a trick, and the trick is to exactly use mapped type. Pay attention that I can take your code and put into mapped type and it doesn't complain anymore:

type DeepFlat<T extends any[]> = {
  [K in keyof T] : T[number] extends any[] ? DeepFlat<T[number]> : T[number]
}[0] // 0 means we now that it will be evaluated for the first item also fully
Collapse
 
dwjohnston profile image
David Johnston • • Edited

The bit that I'm confused about is this: [number].

You say:

[number] at the end has a purpose of getting all value types of produced type

How is this exactly working?

Edit: Had a lot more of a playaround in this post I'd care to hear your thoughts there.

Collapse
 
alextsk profile image
alextsk •

my naive version was really naive

type NaiveFlat<T extends any[][]> = T[number][number]

in my defence, task said tuple of tuples)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️