Typescript Tricks: Defining Types from Arrays
Typescript
When working with TypeScript, it’s common to need a type that represents a union of specific string values. There’s a useful technique for this.
Consider a scenario where you have a set of known string constants, such as feature flags, status codes, or a list of allowed options. A common approach is to define them in an array:
const flags = ["a", "b", "c"];
To create a type representing one of these strings – “a”, “b”, or “c” – you could define it manually:
type Flag = "a" | "b" | "c";
This approach works, but if the array changes, the type definition must also be updated. This introduces a risk of inconsistencies and requires manual synchronization.
The as const
assertion offers a solution. Appending as const
to an array definition instructs TypeScript to treat the array and its contents as literal and immutable.
const flags = ["a", "b", "c"] as const;
Using as const
, TypeScript infers the type of flags
as readonly ["a", "b", "c"]
instead of string[]
. Each element is then treated as its own literal type.
This allows the union type to be derived directly from the constant array:
type Flag = (typeof flags)[number];
Here’s an explanation of the syntax:
typeof flags
: This operator returns the type of theflags
constant, which isreadonly ["a", "b", "c"]
.[number]
: This is an indexed access type. When applied to an array or tuple type, it yields a union of the types of its elements.
Consequently, (typeof flags)[number]
resolves to "a" | "b" | "c"
. This creates a type that is synchronized with the array. If a new flag, such as “d”, is added to the flags
array (with as const
), the Flag
type will automatically include “d”.
This technique is useful for maintaining a single source of truth for a set of related string constants and their corresponding union type. It promotes the DRY (Don’t Repeat Yourself) principle and reduces the likelihood of errors when the constants are modified.