TypeScript enum alternative

Define a list of strings that represents a set of entities, then compute types and key/value object util

Let’s start from a list of string that represent a set of entities, for example the fields of a table:

const fields = [
  'startDate',
  'endDate',
  'price',
  'quantity',
]

My goal is to have an object like the following

const field = {
  startDate: 'startDate',
  endDate: 'endDate',
  price: 'price',
  quantity: 'quantity',
}

As well as a type equivalent to

type Field = 'startDate' | 'endDate' | 'price' | 'quantity'

but of course I want to make it DRY (avoid code repetitions).

I will use this utility function: listToKeyValues.

/**
 * Convert a list of strings to a key/value object.
 *
 * ['a', 'b'] ---> { a: 'a', b: 'b' }
 */
 export function listToKeyValues<T extends string> (list: readonly T[]): {[key: string]: T} {
  return list.reduce((obj: {[key: string]: T}, key: T) => ({...obj, [key]: key}), {})
}

Then the whole implementation will be the following:

export const fields = [
  'startDate',
  'endDate',
  'price',
  'quantity',
] as const

export type Field = typeof fields[number]

export const field = listToKeyValues<Field>(fields)

If for example I create a field with a wrong value I will get an error:

const myField = 'starDate'
// TS will complain with message like:
// Type'starDate' is not assignable to 'startDate' | 'endDate' | 'price' | 'quantity'

The field and fields are readonly and can be used in several ways in a code base. For example, just to mention one use case, I often use them in field labels translations.

Please notice that to lint the code above you need to configure your ESLint adding "parser": "@typescript-eslint/parser".