-
-
Notifications
You must be signed in to change notification settings - Fork 679
Add UnionMember type
#1368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
sindresorhus
merged 19 commits into
sindresorhus:main
from
taiyakihitotsu:add/last-of-union-20260204
Mar 18, 2026
+107
−16
Merged
Add UnionMember type
#1368
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
f0dfb6a
feat: export `LastOfUnion`
taiyakihitotsu 8f776de
Update last-of-union.d.ts
sindresorhus 596d6db
Update readme.md
taiyakihitotsu cb52bd9
Update test-d/last-of-union.ts
taiyakihitotsu 68c7e28
Update test-d/last-of-union.ts
taiyakihitotsu ee67ecc
refactor: add new line
taiyakihitotsu 818f8a3
refactor: use `IsNever`, remove unused tests
taiyakihitotsu b7d6616
refactor: rename `LastOfUnion` to `UnionMember`
taiyakihitotsu 085d1d5
test: add UnionToTupleWithExclude instead of import UnionMember
taiyakihitotsu 89c0f06
test: ensure pick only one member
taiyakihitotsu 4ced771
doc: update comments
taiyakihitotsu 1c642f4
test: fix readonly case, rename unused test cases
taiyakihitotsu 777dc54
test: remove unused test
taiyakihitotsu fe03131
doc: rewrite `UnionMember` example
taiyakihitotsu 12cd358
test: cleanup
som-sm 0c12aef
test: add more cases
som-sm 7295008
test: add comment for `{a: 0} | {readonly a: 0}` case
som-sm eb1e0b7
fix: README
som-sm 0aacdc4
Merge branch 'main' into add/last-of-union-20260204
taiyakihitotsu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import type {UnionToIntersection} from './union-to-intersection.d.ts'; | ||
| import type {IsNever} from './is-never.d.ts'; | ||
|
|
||
| /** | ||
| Returns a member of a union type. Order is not guaranteed. | ||
|
|
||
| Returns `never` when the input is `never`. | ||
|
|
||
| @example | ||
| ``` | ||
| import type {UnionMember} from 'type-fest'; | ||
|
|
||
| type Last = UnionMember<1 | 2 | 3>; | ||
| //=> 3 | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| type LastNever = UnionMember<never>; | ||
| //=> never | ||
| ``` | ||
|
|
||
| @see https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468375328 | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Use-cases: | ||
| - Implementing recursive type functions that accept a union type. | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Reducing a union one member at a time, for example when building tuples. | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| It can detect a termination case using {@link IsNever `IsNever`}. | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @example | ||
| ``` | ||
| import type {UnionMember, IsNever} from 'type-fest'; | ||
|
|
||
| type UnionToTuple<T, L = UnionMember<T>> = | ||
| IsNever<T> extends false | ||
| ? [...UnionToTuple<Exclude<T, L>>, L] | ||
| : []; | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| @category Type | ||
| */ | ||
| export type UnionMember<T> = | ||
| IsNever<T> extends true | ||
| ? never | ||
| : UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) | ||
| ? R | ||
| : never; | ||
|
|
||
| export {}; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import {expectType} from 'tsd'; | ||
| import type {UnionMember, IsNever} from '../index.d.ts'; | ||
|
|
||
| // `UnionMember` distinguishes between different modifiers. | ||
| type UnionType = {a: 0} | {b: 0} | {a?: 0} | {readonly a?: 0} | {readonly a: 0}; | ||
| expectType<true>({} as UnionMember<UnionType> extends UnionType ? true : false); | ||
| expectType<false>({} as UnionType extends UnionMember<UnionType> ? true : false); | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // `never` acts as a termination condition with `IsNever`. | ||
| expectType<never>({} as UnionMember<never>); | ||
|
|
||
| expectType<unknown>({} as UnionMember<unknown>); | ||
| expectType<any>({} as UnionMember<any>); | ||
|
|
||
| // Ensure exactly one member is selected at a time, while covering all members in the union. | ||
| type UnionToTupleWithExclude<T, L = UnionMember<T>> = | ||
| IsNever<T> extends false | ||
| ? UnionToTupleWithExclude<Exclude<T, L>> | [L] | ||
| : never; | ||
| expectType<[1] | [2] | [3]>({} as UnionToTupleWithExclude<1 | 2 | 3>); | ||
taiyakihitotsu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.