Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

as a person that never touched JS and TS... what's the difference between the two answers?


For one, the simple answer is incomplete. It gives the fully unwrapped type of the array but you still need something like

  type FlatArray<T extends unknown[]> = Flatten<T[number]>[]
The main difference is that the first, rest logic in the complex version lets you maintain information TypeScript has about the length/positional types of the array. After flattening a 3-tuple of a number, boolean, and string array TypeScript can remember that the first index is a number, the second index is a boolean, and the remaining indices are strings. The second version of the type will give each index the type number | boolean | string.


First one flattens a potentially-nested tuple type. E.g., FlatArr<[number, [boolean, string]]> is [number, boolean, string].

Second one gets the element type of a potentially-nested array type. E.g., Flatten<number[][]> is number.

For what it's worth, I've never needed to use either of these, though I've occasionally had other uses for slightly fancy TypeScript type magic.


The answer above actually gets the type union of all non-array elements of a multi-level array.

In other words

    Flatten<[1,[2,'a',['b']]]>
will give you a union type of 1, 2, 'a', and 'b'

    const foo: Flatten<[1,[2,'a',['b']]]> = 'b'; // OK
    const bar: Flatten<[1,[2,'a',['b']]]> = 'c'; // Error: Type '"c"' is not assignable to type '1 | 2 | "a" | "b"'
Technically the inference is unnecessary there, if that's you're goal:

     type Flatten<T> = T extends Array<unknown> ? Flatten<T[number]> : T
I don't really consider this the type of flattening an array, but `Array<Flatten<ArrType>>` would be. And this would actually be comparable to the builtin Array.prototype.flat type signature with infinite depth (you can see the typedef for that here[1], but this is the highest level of typescript sorcery)

My solution was for flattening an array with a depth of 1 (most people using Array.prototype.flat are using this default depth I'd wager):

    console.log(JSON.stringify([1,[2, [3]]].flat()));
    > [1,2,[3]]
The type I provided would match those semantics:

    // 'readonly' added to the 'extends' sections to work on "as const" (readonly) arrays
    type FlatArr<Arg extends unknown[] | readonly unknown[]> = Arg extends readonly [infer First, ...(infer Rest)] ?
      First extends readonly unknown[] ?
        [...First, ...FlatArr<Rest>] :
        [First, ...FlatArr<Rest>] :
      [];

    const flatten = <Arr extends unknown[] | readonly unknown[]>(arr: Arr): FlatArr<Arr> => arr.flat() as FlatArr<Arr>
    const someArr = [1,[2,'a',['b', ['c']]]] as const; // const someArr: readonly [1, readonly [2, "a", readonly ["b", readonly ["c"]]]]
    const someArr2 = flatten(someArr);                 // const someArr2: [1, 2, "a", readonly ["b", readonly ["c"]]]

    
[1]: https://github.com/microsoft/TypeScript/blob/main/src/lib/es...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: