ยท Jake Worth
Union type from array in TypeScript
I often want the same string list in three places: runtime values, a state type, and function parameters. Duplicating a union by hand drifts out of sync:
const builderSteps = ['communications', 'estimates', 'procedures'];type BuilderStep = 'communications' | 'estimates' | 'procedures'; // Better keep this up-to-date! ๐คWith as const and an indexed access type, the array is the single source of
truth:
const builderSteps = ['communications', 'estimates', 'procedures'] as const;
type BuilderStep = (typeof builderSteps)[number];// Returns a union of 'communications' | 'estimates' | 'procedures'This lets us type a slice of state, type a function that might receive that state, and build components all from the same JavaScript array.
import {useState} from 'react';
// `as const` arrayconst builderSteps = ['communications', 'estimates', 'procedures'] as const;
// Indexed access typetype BuilderStep = (typeof builderSteps)[number];
export const Component = () => { // Use case #1: state const [step, setStep] = useState<BuilderStep>('communications');
// Use case #2: function typing const handleStepClick = (step: BuilderStep) => setStep(step);
// Use case #3: building UI return ( <> {builderSteps.map((step) => ( <button key={step} onClick={() => handleStepClick(step)}> Set step: {step} </button> ))} </> );};When we skip as const, TypeScript widens the array to string[]. Then
(typeof builderSteps)[number] becomes string, and you lose the union. The
const assertion freezes the elements as read-only literal types so the
[number] lookup can turn the array into a union.
- Const Assertions โ TypeScript Handbook