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