UnionMember: export internal type, useful to recurse union-types. #1368
UnionMember: export internal type, useful to recurse union-types. #1368taiyakihitotsu wants to merge 10 commits intosindresorhus:mainfrom
UnionMember: export internal type, useful to recurse union-types. #1368Conversation
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
|
Thanks for your reviews! And I believe this suggestion implies that If so, type UnionToTupleWithExclude<T, L = LastOfUnion<T>> =
IsNever<T> extends false
? [...UnionToTupleWithExclude<Exclude<T, L>>, L]
: [];
expectType<1 | 2 | 3>({} as UnionToTupleWithExclude<1 | 2 | 3>[number]); |
test-d/last-of-union.ts
Outdated
| : never; | ||
|
|
||
| type DifferentModifierUnion = {readonly a: 0} | {a: 0}; | ||
| expectType<DifferentModifierUnion>({} as UnionToTuple<DifferentModifierUnion>[number]); |
There was a problem hiding this comment.
Umm...I don't think this approach for testing is ideal where we implement something with LastOfUnion and then test the implementation. We'll have to find a better way.
There was a problem hiding this comment.
Let's just use something similar to UnionToTuple, like:
type Test<T, L = LastOfUnion<T>> =
IsNever<T> extends false
? Test<Exclude<T, L>> | L
: never;
expectType<1 | 2 | 3>({} as Test<1 | 2 | 3>);Also, remove everything else. Here, we are not trying to test quirks of a specific implementation, such as Exclude behaving unexpectedly in certain cases. The goal here is to test LastOfUnion only, so the surrounding implementation should be as minimal as possible.
There was a problem hiding this comment.
type Test<T, L = LastOfUnion<T>> = IsNever<T> extends false ? Test<Exclude<T, L>> | L : never;
Even this is not ideal, because it does not guarantee that LastOfUnion picks exactly one member at a time. For example, in the 1 | 2 | 3 case, if LastOfUnion<1 | 2 | 3> returns 1 | 2 first, and then LastOfUnion<Exclude<1 | 2 | 3, 1 | 2>> returns 3, the test would still pass.
This is a better approach I guess:
type Test<T, L = LastOfUnion<T>> =
IsNever<T> extends false
? Test<Exclude<T, L>> | [L]
: never;
expectType<[1] | [2] | [3]>({} as Test<1 | 2 | 3>);I hope this makes sense?
There was a problem hiding this comment.
I kept only this definition.
#1368 (comment)
After both are merged, #1368 and #1349, then open a PR to fix test-d/union-member.ts to add the readonlyExcludeExactly case.
Or include the test case into #1349 (and merge this after #1368).
Does this sound acceptable?
If so, which approach do you prefer?
There was a problem hiding this comment.
Sorry, our comments crossed!
Even this is not ideal, because it does not guarantee that LastOfUnion picks exactly one member at a time.
I’ve added your test case to test-d/union-member.ts and included a comment explaining the scenario it covers.
LastOfUnion: export internal type, useful to recurse union-types. UnionMember: export internal type, useful to recurse union-types.
Add
UnionMemberAnd the current
UnionMember, which locally defined inUnionToTuple, returnsunknown.This would cause an error if end-users use
IsNeverfor termination check.Ideally,$A$ and $B$ are type arguments of $A \cup B$ -> $B$ , so $\emptyset$ -> $\emptyset$ intuitively ($\bot$ -> $\bot$ ).
UnionMemberreduces a type, e.g., assumingUnionMember<A | B>,This PR's
UnionMemberhandles this.Note
UnionToTuple: Fix behavior when a union member is a supertype of another; AddExcludeExactlytype #1349