Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript exhaustive array that contains all properties from a given enum

I have an enum in TypeScript.

I want to write a function that returns an exhaustive array that contains exactly one reference to each property on the enum.

The return of the function will be an Array<{ id: EnumValue, label: string }>.

Sample code:

// I have this enum:
enum StepId {
  STEP_ONE = 'step-one-id',
  STEP_TWO = 'step-two-id',
}


// I want to define some function...
function someFunc() {
  return ...
}


// which returns an Array where every value from `StepId` is represented as an object with that value as the `id` field.
someFunc() === [
  { id: CustomStepIds.STEP_ONE, label: 'Step One', },
  { id: CustomStepIds.STEP_TWO, label: 'Step Two', },
];

I have tried to first solve the problem of enforcing all enum values are represented in an array. I've tried this:

enum CustomStepId {
  STEP_ONE = 'step-one-id',
  STEP_TWO = 'step-two-id',
}

type ValueOf<T> = T[keyof T];

const stepConfig: Array<{ id: ValueOf<typeof CustomStepId>, label: string }> = [
  { id: CustomStepId.STEP_ONE, label: 'Step One', },
  { id: CustomStepId.STEP_TWO, label: 'Step Two', },
  { id: CustomStepId.STEP_TWO, label: 'Step Two', }, // this should error, as STEP_TWO is already defined
];

However as noted in the comment above, while this enforces id is a CustomStepId, it does not enforce uniqueness of the field.

like image 291
tdc Avatar asked Dec 10 '25 08:12

tdc


1 Answers

I propose the following, on the basis that I (and much of the Typescript world) eschew Enums in favour of const arrays See typescript playground

I drafted mechanisms based on a map or a tuple, depending what you prefer or need.

type MemberOf<Arr extends readonly unknown[]> = Arr[number]

const STEP_IDS = ['step-one-id', 'step-two-id'] as const;
type StepId = MemberOf<typeof STEP_IDS>;

type StepConfigMap = {[k in StepId]: {
    label:string
}}

type ConfigTuple<Tuple extends readonly [...unknown[]]> = {
  [Index in keyof Tuple]: {
    id:Tuple[Index],
    label:string
  };
} & {length: Tuple['length']};

type StepConfigTuple = ConfigTuple<typeof STEP_IDS>;

const CONFIG_MAP: StepConfigMap  = {
    "step-one-id":{
        label:"something"
    },
    "step-two-id":{
        label:"something else"
    },
    // errors as it's already defined
    // An object literal cannot have multiple properties with the same name.(1117)
    "step-two-id":{
        label:"something else"
    }
} as const;

const CONFIG_TUPLE = [
    {
        id:"step-one-id",
        label:"something",
    },
    {
        id:"step-two-id",
        label:"something",
    },
    // errors as it's already defined
    // Source has 3 element(s) but target allows only 2.
    {
        id:"step-two-id",
        label:"something",
    }

] as const satisfies StepConfigTuple;
like image 184
cefn Avatar answered Dec 12 '25 01:12

cefn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!