TypeScript: Turn an Array of Values into a Type Union

I found the answer I was looking for here: https://dev.to/shakyshane/2-ways-to-create-a-union-from-an-array-in-typescript-1kd6

I wasn't sure that it would be possible. In fact, it turns out that the solution is far more impressive than I expected.

What follows cuts to the core part of the article that I need solved my particular problem.

Problem

I want to have an object type with specific keys and I want a static array of those keys, but I don't want to have the same list of keys written out twice in case I mistakenly make them differ in the future.

type K = "a" | "b" | "c";
type O = Record<K, V>;
const keys: K[] = ["a","b","c"];

I want to be sure that 'keys' contains every member of type 'K' exactly once and vice-versa. I want to use 'keys' to build an object of type 'O', so I can't just rely on using 'Object.keys' on an existing object.

Solution

const keys = ["a","b","c"] as const;
type K = typeof keys[number];
type O = Record<K, V>;

Use 'as const' to make the type of  'keys' be interpreted as a readonly tuple of strings, whereas before it was an array, 'K[]', which could have any members of 'K' anywhere zero or more times each.

Because 'keys' is a value, it needs to be prefixed with 'typeof' to use its type in a type expression.

By indexing a tuple type with a specific number, you can get the type at that position in the tuple.

The incredible part: By indexing the tuple type with a union of numbers, you get the union of the types at those positions. Therefore by indexing 'typeof keys', which is a tuple, with 'number', which is the union of all numbers, you get the union of all strings in 'keys'.

type T = ["a", "b", "c"][0]; // "a"
type T = ["a", "b", "c"][0 | 1]; // "a" | "b"
type T = ["a", "b", "c"][number]; // "a" | "b" | "c"

Comments

Popular posts from this blog

The TypeScript "extends" Keyword Doesn't Extend Types