diff --git a/index.d.ts b/index.d.ts index 8568b462a..472d37dd8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -86,6 +86,7 @@ export type {WritableKeysOf} from './source/writable-keys-of'; export type {HasWritableKeys} from './source/has-writable-keys'; export type {Spread} from './source/spread'; 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 55e5e8684..a1b9786a6 100644 --- a/readme.md +++ b/readme.md @@ -247,6 +247,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 tuple 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..c2ccc5671 --- /dev/null +++ b/source/union-to-tuple.d.ts @@ -0,0 +1,30 @@ +import type {UnionToIntersection} from './union-to-intersection'; + +/** + Convert a union type into a 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 + ``` + const pets = { + dog: '🐶', + cat: '🐱', + snake: '🐍', + }; + + type Pet = keyof typeof pets; + //=> "dog" | "cat" | "snake" + + const petList = Object.keys(pets) as UnionToTuple; + //=> ["dog", "cat", "snake"] + ``` + + @category Array + */ + +export type UnionToTuple = UnionToIntersection< +Tuple extends never ? never : (_: Tuple) => Tuple +> extends (_: never) => infer LastTupleElement + ? [...UnionToTuple>, LastTupleElement] + : []; diff --git a/test-d/union-to-tuple.ts b/test-d/union-to-tuple.ts new file mode 100644 index 000000000..8c7573d5e --- /dev/null +++ b/test-d/union-to-tuple.ts @@ -0,0 +1,12 @@ +import {expectAssignable, expectError, expectType} from 'tsd'; +import type {UnionToTuple} from '../index'; + +type Options = UnionToTuple<'a' | 'b' | 'c'>; + +const options: Options = ['a', 'b', 'c']; + +expectAssignable(options); +expectType<'a'>(options[0]); +expectType<'b'>(options[1]); +expectType<'c'>(options[2]); +expectError(options[0] = 'b');