Deriving a union type from a string array in Typescript
I've run into a few occasions where I require the same data at both compile-time and run-time. Usually this occurs when I'm writing some sort of React Higher-Order Component:
const myData = ["foo", "bar", "baz"]
interface Props {
myData: "foo" | "bar" | "baz"
}
class MyClassCore<Props> { ... }
export const MyClass withMyData(mydata, MyClassCore);
In the above contrived example, I need to maintain both the string array and the union type. An improvement would be to derive the union type from the array so that if we need to update the data, we would only need to update the source array and the changes would apply to the union type automatically.
The easiest way to do this is by first creating an immutable tuple of literals using the as const
assertion.
const myData = ["foo", "bar", "baz"] as const; // readonly ["foo", "bar", "baz"]
From here you have a couple ways to derive the union type. The first is to "use the property type from the numeric index signature" with this syntax:
type MyDataUnion = myData[number]; // "foo" | "bar" | "baz"
A colleague had another way of doing the above that reads a little more straightforward to me:
type ExtractArrayItemType<T> = T extends ArrayLike<infer U> ? U : never;
type MyDataUnion = ExtractArrayItemType<typeof myData>; // "foo" | "bar" | "baz"
Because the array is immutable, Typescript can safely infer that the type is "foo" | "bar" | "baz"
- the narrowest type - instead of string[]
or even ("foo" | "bar" | "baz")[]
as those types are mutable.