diff --git a/index.d.ts b/index.d.ts index e17eddca5..642e3c8e0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -104,6 +104,7 @@ export type {Spread} from './source/spread'; export type {IsInteger} from './source/is-integer'; export type {IsFloat} from './source/is-float'; export type {TupleToUnion} from './source/tuple-to-union'; +export type {UnionToTuple} from './source/union-to-tuple'; export type {IntRange} from './source/int-range'; export type {IsEqual} from './source/is-equal'; export type { diff --git a/readme.md b/readme.md index 306e9f794..1e69ad507 100644 --- a/readme.md +++ b/readme.md @@ -283,6 +283,7 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; - [`MultidimensionalReadonlyArray`](source/multidimensional-readonly-array.d.ts) - Create a type that represents a multidimensional readonly array of the given type and dimensions. - [`ReadonlyTuple`](source/readonly-tuple.d.ts) - Create a type that represents a read-only tuple of the given type and length. - [`TupleToUnion`](source/tuple-to-union.d.ts) - Convert a tuple/array into a union type of its elements. +- [`UnionToTuple`](source/union-to-tuple.d.ts) - Convert a union type into a unsorted tuple/array type of its elements. ### Numeric diff --git a/source/union-to-tuple.d.ts b/source/union-to-tuple.d.ts new file mode 100644 index 000000000..819df7d0e --- /dev/null +++ b/source/union-to-tuple.d.ts @@ -0,0 +1,54 @@ +import type {IsNever} from './is-never'; +import type {UnionToIntersection} from './union-to-intersection'; + +/** +Returns the last element of a union type. + +@example +``` +type Last = LastOfUnion<1 | 2 | 3>; +//=> 3 +``` +*/ +type LastOfUnion = +UnionToIntersection T : never> extends () => (infer R) + ? R + : never; + +/** +Convert a union type into a unsorted tuple/array type of its elements. + +This can be useful when you have objects with a finite set of keys and want a type defining only the allowed keys, but do not want to repeat yourself. + +@example +``` +import type {UnionToTuple} from 'type-fest'; + +type Numbers = 1 | 2 | 3; +type NumbersTuple = UnionToTuple; +//=> [1, 2, 3] +``` + +@example +``` +import type {UnionToTuple} from 'type-fest'; + +const pets = { + dog: '🐶', + cat: '🐱', + snake: '🐍', +}; + +type Pet = keyof typeof pets; +//=> "dog" | "cat" | "snake" + +const petList = Object.keys(pets) as UnionToUnsortedTuple; +//=> ["dog", "cat", "snake"] +``` + +@category Array +*/ +export type UnionToTuple> = +IsNever extends false + ? [...UnionToTuple>, L] + : []; diff --git a/test-d/union-to-tuple.ts b/test-d/union-to-tuple.ts new file mode 100644 index 000000000..c7b4f2678 --- /dev/null +++ b/test-d/union-to-tuple.ts @@ -0,0 +1,13 @@ +import {expectAssignable, expectError, expectType} from 'tsd'; +import type {UnionToTuple} from '../index'; + +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); +expectType({} as ('a' | 'b' | 'c')); + +type Options1 = UnionToTuple<1 | 2 | 3>; +expectType({} as (1 | 2 | 3)); + +type Options2 = UnionToTuple; +expectType({} as (1 | false | true));