I currently have both an array of strings and a string literal union type containing the same strings:
const furniture = [\'chair\', \'table\', \'lamp\'];
ty
The best available workaround:
const furnitureObj = { chair: 1, table: 1, lamp: 1 };
type Furniture = keyof typeof furnitureObj;
const furniture = Object.keys(furnitureObj) as Furniture[];
Ideally we could do this:
const furniture = ['chair', 'table', 'lamp'];
type Furniture = typeof furniture[number];
Unfortunately, today furniture
is inferred as string[]
, which means Furniture
is now also a string
.
We can enforce the typing as a literal with a manual annotation, but it brings back the duplication:
const furniture = ["chair", "table", "lamp"] as ["chair", "table", "lamp"];
type Furniture = typeof furniture[number];
TypeScript issue #10195 tracks the ability to hint to TypeScript that the list should be inferred as a static tuple and not string[]
, so maybe in the future this will be possible.
With the use of generic rest parameters, there is a way to correctly infer string[]
as a literal tuple type and then get the union type of the literals.
It goes like this:
const tuple = <T extends string[]>(...args: T) => args;
const furniture = tuple('chair', 'table', 'lamp');
type Furniture = typeof furniture[number];
More about generic rest parameters
TypeScript version 3.4 has introduced so-called const contexts, which is a way to declare a tuple type as immutable and get the narrow literal type directly (without the need to call a function like shown above).
With this new syntax, we get this nice concise solution:
const furniture = ['chair', 'table', 'lamp'] as const;
type Furniture = typeof furniture[number];
More about the new const contexts is found in this PR as well as in the release notes.
The only adjustement I would suggest is to make the const
guaranteed compatible with the type, like this:
type Furniture = 'chair' | 'table' | 'lamp';
const furniture: Furniture[] = ['chair', 'table', 'lamp'];
This will give you a warning should you make a spelling error in the array, or add an unknown item:
// Warning: Type 'unknown' is not assignable to furniture
const furniture: Furniture[] = ['chair', 'table', 'lamp', 'unknown'];
The only case this wouldn't help you with is where the array didn't contain one of the values.