Context

You want to declare an array of options. It should not change during the runtime of your program. To enforce non-mutability you declare it using const assertion.

const options = ["Foo", "Bar", "Baz"] as const;

const assertion means that you ask compiler to use the narrowest or most specific type. So options is not string[] but a readonly, 3-elements tuple.

Problem

Now you want to create a type that reflects literal strings listed in this options tuple.

You could do it in the following way:

type Options = "Foo" | "Bar" | "Baz";

but this solution has a defect: you need to define each possible string literal twice.

Is there a way to infer the Options type based on options tuple?

Solution

This is where “Lookup Type” comes to the rescue.

What is “lookup type”?

In general: it allows you to i.e. extract typing of a single attribute of some structure using element access notation.

Example: lookup of the typing of an attribute name in Person structure.

interface Person {
  name: string;
  age: number;
}

type PersonName = Person["name"]; // This is "lookup type".
//   ^ Type is: string

For a 3-element tuple options you can access values either using options[0], or options[1], or options[2].

To access type of each of the values, you can use the same syntax over typeof options - the type of this tuple.

type OptionAtIdx0 = typeof options[0];
//   ^ Type is: "Foo"

It also works with unions i.e. type at index 0, or 1, or 2 - 0 | 1 | 2.

type Options = typeof options[0 | 1 | 2];
//   ^ Type is: "Foo" | "Bar" | "Baz"

To make it “future proof”, you can replace 0 | 1 | 2 with number. Now, any time you add extra item to options tuple, Options type will automatically be updated.

You can think of if as of “possible types you can get by accessing value with given type using any valid number

type Options = typeof options[number];
// ^ This is equivalent to: "Foo" | "Bar" | "Baz"

Voilà, now Options type derives from options array so when adding a new option, you only need to add new string to options.

Open this TS playground to see code for this write-up. To find out more about Lookup Types, check TypeScript docs