type-fest icon indicating copy to clipboard operation
type-fest copied to clipboard

Add `UnionToUnorderedTuple` type

Open kopach opened this issue 9 months ago • 9 comments

kopach avatar Sep 13 '23 19:09 kopach

Thanks for contributing.

It would be great if you could check out the previous attempt at adding this type and address the feedback given there (if relevant): https://github.com/sindresorhus/type-fest/pull/167

sindresorhus avatar Sep 14 '23 06:09 sindresorhus

hi @sindresorhus , thanks for pointing this out. Yes indeed this implementation has exactly the same issues as during previous attempt. The problem is in TypeScript itself. It doesn't guarantee that tuple will always preserve order of it's elements. Closing this for now

kopach avatar Sep 14 '23 09:09 kopach

hi @sindresorhus, just found this https://github.com/microsoft/TypeScript/issues/44116#issuecomment-849833071

Unions are unordered and any code that attempts to observe a union's order is asking for undefined behavior.

So, maybe we shouldn't expect tuple to be ordered in the same way as union? Personally, I would have this UnionToTuple type as it is (possibly with additional comment in docs, that order could change). I use this type in my codebase and still find it useful.

kopach avatar Sep 14 '23 14:09 kopach

Maybe just renaming this to UnionToUnsortedTuple to indicate this is not sorted?

kopach avatar Sep 14 '23 18:09 kopach

Yeah. I would go with UnionToUnorderedTuple.

sindresorhus avatar Sep 28 '23 17:09 sindresorhus

Bump :)

sindresorhus avatar Oct 25 '23 10:10 sindresorhus

:+1:

mkovel avatar Nov 14 '23 00:11 mkovel

hi @sindresorhus, @mkovel. I started refactoring this and discovered, that there are other conceptual problems with this type. Not sure what should be the correct result in this case

type T = UnionToUnsortedTuple<boolean | 'a'>; // [false, true, 'a']

There are many more issues & attempts to solve them here: https://github.com/microsoft/TypeScript/issues/13298 Not sure if we should have such a type in the library. TS should resolve this. What do you think?

kopach avatar Nov 22 '23 21:11 kopach

@kopach Hi, honestly I didn't dive into it deeply. I just reused this code to solve the issue. Maybe it will be helpful.

export type UnionToIntersection<U> = (
  U extends never ? never : (arg: U) => never
) extends (arg: infer I) => void
  ? I
  : never;

export type UnionToTuple<T> = UnionToIntersection<
  T extends never ? never : (t: T) => T
> extends (_: never) => infer W
  ? [...UnionToTuple<Exclude<T, W>>, W]
  : [];

mkovel avatar Nov 23 '23 11:11 mkovel

@kopach Hi, honestly I didn't dive into it deeply. I just reused this code to solve the issue. Maybe it will be helpful.

export type UnionToIntersection<U> = (
  U extends never ? never : (arg: U) => never
) extends (arg: infer I) => void
  ? I
  : never;

export type UnionToTuple<T> = UnionToIntersection<
  T extends never ? never : (t: T) => T
> extends (_: never) => infer W
  ? [...UnionToTuple<Exclude<T, W>>, W]
  : [];

This is by far one of the most amazing types I've ever seen. It's exactly what I needed, and I don't really care much about the undefined behavior aspect. It's just for type checking and cli configuration.

Thank you so much.

jtenner avatar Feb 21 '24 20:02 jtenner

I think it's still worth adding this type, but this PR is not moving forward, so closing for now: https://github.com/sindresorhus/type-fest/issues/819

sindresorhus avatar Feb 22 '24 06:02 sindresorhus