Comment by pcthrowaway

Comment by pcthrowaway 4 hours ago

21 replies

> Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail.

To be clear, an array flat type:

    type FlatArr<Arg extends unknown[]> = Arg extends [infer First, ...(infer Rest)] ?
      First extends unknown[] ?
        [...First, ...FlatArr<Rest>] :
        [First, ...FlatArr<Rest>] :
      [];
is far from basic Typescript. The average Typescript dev likely doesn't need to understand recursive conditional types. It's a level of typescript one typically only needs for library development.

Not only have I never been expected to write something like this for actual work, I'm not sure it's been useful when I have, since most of my colleagues consider something like this nerd sniping and avoid touching/using such utilities, even with documentation.

wk_end 4 hours ago

If I saw that in a PR I would push very hard to reject; something like that is a maintenance burden that probably isn’t worth the cost, and I’ve been the most hardcore about types and TypeScript of anyone of any team I’ve been on in the past decade or so.

Now, that said, I probably would want to be friends with that dev. Unless they had an AI generate it, in which case the sin is doubled.

  • probabletrain 3 hours ago

    I think there’s a difference between what’s expected/acceptable for library code vs application code. Types like this might be hard to understand, but they create very pleasant APIs for library consumers. I’ve generally found it very rare that I’ve felt the need to reach for more complex types like this in application code, however.

    RXJS’s pipe function has a pretty complex type for its signature, but as a user of the library it ‘just works’ in exactly the type-safe way I’d expect, without me having to understand the complexity of the type.

  • ibejoeb 3 hours ago

    If it's correct, it's not a maintenance nightmare, and it will alert you to problems later when someone wants to use it incorrectly.

    If you're writing first-party software, it probably doesn't matter. But if you have consumers, it's important. The compiler will tell you what's wrong all downstream from there unless someone explicitly works around it. That's the one you want to reject.

  • [removed] 4 hours ago
    [deleted]
  • 8note 4 hours ago

    looking back at them is also real hard to debug. you dont get a particularly nice error message, and a comment or a test would tell better than the type what the thing should be looking like

  • spankalee 3 hours ago

    What's the alternative? Have incorrect types for the function? That's not better.

    • wk_end 2 hours ago

      To answer this we probably need more details, otherwise it's gonna be an XY Problem. What is it that I'm trying to do? How would I type this function in, say, SML, which isn't going to allow incorrect types but also doesn't allow these kinds of type gymnastics?

      • spankalee 2 hours ago

        We don't have to deal in hypotheticals - we have a concrete example here. There's a method, array.flat() that does a thing that we can correctly describe in TypeScript's type system.

        You say you would reject those correct types, but for what alternative?

        It's hugely beneficial to library users to automatically get correctly type return values from functions without having to do error-prone casts. I would always take on the burden of correct types on the library side to improve the dev experience and reduce the risk of bugs on the library-consumption side.

    • epolanski 2 hours ago

      The alternative is what shows in the comment: go on HN and tell the world you think TS and JS are crap and it's not worth your time, while writing poor software.

epolanski 2 hours ago

The version I was thinking when I wrote the comment is simpler

    type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T
> The average Typescript dev likely doesn't need to understand recursive conditional types.

The average X dev in Y language doesn't need to understand Z is a poor argument in the context of writing better software.

  • furyofantares an hour ago

    > The average X dev in Y language doesn't need to understand Z is a poor argument in the context of writing better software.

    It's a good response to the claim that we'd be surprised at how many would fail to do this, though.

  • NooneAtAll3 2 hours ago

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

    • granzymes an hour ago

      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.
    • ameliaquining an hour ago

      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.

miki123211 3 hours ago

I recently had to write a Promise.all, but using an object instead of an array.

That was... non-trivial.

  • hdjrudni 3 hours ago

    If it's what I'm thinking, that one isn't too bad. I wrote it awhile back:

        export async function promiseAll<T extends Record<string, Promise<any>>>(promises: T): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
            const keys = Object.keys(promises) as Array<keyof T>;
            const result = await Promise.all(keys.map(key => promises[key]));
            return Object.fromEntries(result.map((value, i) => [keys[i], value])) as { [K in keyof T]: Awaited<T[K]> };
kaoD 4 hours ago

For those unfamiliar with TS, the above is just...

    function flat([head, ...tail]) {
      return Array.isArray(head)
        ? [...flat(head), ...flat(tail)]
        : [head, ...flat(tail)]
    }
...in TS syntax.
  • tomsmeding 3 hours ago

    Well, it is the type of that, in TS syntax. Few are the statically-typed languages that can even express that type.