TypeScript: Define a union type from an array of strings

Typescript

Typescript Problem Overview


I can't be the first person coming across this, but my searches have not turned up any useful leads yet. Would greatly appreciate some expert TypeScript advice.

Say I have an array:

const fruits = ["Apple", "Orange", "Pear"];

and I want to define an object mapping each fruit to some fun facts about it:

interface Facts {
    color: string,
    typicalWeight: number
}

const fruitFacts: { [key: members of fruits]: Facts } = {
    "Apple": { color: "green", typicalWeight: 150 }
    //
}

How do I do that [key: members of fruits] part?

Bonus: How do I enforce that my fruitFacts object exhaust all the keys derived from the array as well, so that it specifies facts for Apples, Oranges, and Pears in the example above.

Typescript Solutions


Solution 1 - Typescript

TypeScript 3.4 added const assertions which allow for writing this as:

const fruits = ["Apple", "Orange", "Pear"] as const;
type Fruits = typeof fruits[number]; // "Apple" | "Orange" | "Pear"

With as const TypeScript infers the type of fruits above as readonly["Apple", "Orange", "Pear"]. Previously, it would infer it as string[], preventing typeof fruits[number] from producing the desired union type.

Solution 2 - Typescript

It can be done but first you need an extra function to help infer the string literal type for the array elements. By default Typescript will infer string[] for an array even if it is a constant. After we have an array of string literal types we can just use a type query to get the desired type

function stringLiteralArray<T extends string>(a: T[]) {
    return a;
}

const fruits = stringLiteralArray(["Apple", "Orange", "Pear"]);
type Fruits = typeof fruits[number]

Since 3.4 you can also use a const type assertion instead of the stringLiteralArray function:

const fruits = ["Apple", "Orange", "Pear"] as const;
type Fruits = typeof fruits[number]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArash MotamediView Question on Stackoverflow
Solution 1 - TypescriptBen RegenspanView Answer on Stackoverflow
Solution 2 - TypescriptTitian Cernicova-DragomirView Answer on Stackoverflow