sindresorhus / type-fest

A collection of essential TypeScript types
Creative Commons Zero v1.0 Universal
14.41k stars 547 forks source link

UnionToTuple fails when tuple is not ordered #955

Closed gxxcastillo closed 2 months ago

gxxcastillo commented 2 months ago

UnionToTuple is requiring a specific order.

type Options = UnionToTuple<'a' | 'b' | 'c'>;

// This is valid
const options: Options = ['a', 'b', 'c'];

// This is failing
const options: Options = ['a', 'c', 'b'];

As a result, I get errors when I want to create an exhaustive array of an object's properties:

type Obj = {a: string; b: number; c: boolean};
type Keys = UnionToTuple<keyof Obj>;

// This is valid
const orderedKeys: Keys = ['a', 'b', 'c'];

// This is failing
const unorderedKeys: Keys = ['a', 'c', 'b'];

This makes it particularly hard to use if the Obj type was, itself, dynamically created as it results in an intermittent type failure no matter what order I put the keys in.

Upvote & Fund

Fund with Polar

Emiyaaaaa commented 2 months ago

See description in #945. Due to the limitations of typescript, it is currently not possible to generate ordered arrays, which we state in the documentation.

gxxcastillo commented 2 months ago

@Emiyaaaaa, I saw the notes in the test and in the docs, I found it confusing, however, because they say UnionToTuple generates "unordered" array - which seems to be exactly what is not supported.

Emiyaaaaa commented 2 months ago

I removed "unordered" from the name, I don't think unordered is necessary in the name, it can be specified in the docs, after all, no one would specifically need to generate an "unordered" array, it's just that the current limitation of ts leads to unordered arrays, so maybe later on it will be possible to optimise the result to be ordered.

If your confuse is about the type name, this info in #945 may can help you.

gxxcastillo commented 2 months ago

Thanks @Emiyaaaaa. I think my confusion comes in because the docs state these are "unordered" arrays, in fact, there's even this test:

type Options = UnionToTuple<'a' | 'b' | 'c'>;
// Results unordered
expectAssignable<['a', 'b', 'c'] | ['a', 'c', 'b'] | ['b', 'a', 'c'] | ['b', 'c', 'a'] | ['c', 'a', 'b'] | ['c', 'b', 'a']>({} as Options);

The issue I'm running into is that this test is actually not valid and should be failing. Note, if you do type Options = UnionToTuple<'a' | 'b' | 'c'>; you will find you actually can not assign ['a', 'c', 'b'] to it.

In short, the docs say "Convert a union type into an unordered tuple type of its elements" but it seems the docs should instead say "Convert a union type into an ordered tuple type of its elements".