49

What is the best way of making a TypeScript type based on an array of strings? I am on version 2.6.2. The array is long and I do not want to repeat myself by duplicating the string values in an Enum declaration.

What I want to do is something like this:

const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
export type Color = convertStringArrayToType(colors);

The following solution (source) works fine, but feels hacky:

/** Utility function to create a K:V from a list of strings */
function strEnum<T extends string>(o: Array<T>): {[K in T]: K} {
  return o.reduce((res, key) => {
    res[key] = key;
    return res;
  }, Object.create(null));
}

/**
  * Sample create a string enum
  */

/** Create a K:V */
const Direction = strEnum([
  'North',
  'South',
  'East',
  'West'
])
/** Create a Type */
type Direction = keyof typeof Direction;
6
  • What's hacky about it ? Hard to understand maybe, but it uses standard advanced types in Typescript, I wouldn't change a thing Commented Jul 25, 2018 at 14:49
  • Note typescript also has string enums in recent versions enum Direction { North = 'North', South = 'South', East = 'East', West = 'West' } but it depends what you are trying to do Commented Jul 25, 2018 at 14:51
  • @TitianCernicova-Dragomir Yes, but I don't want to repeat the text unnecessarily, as I mentioned Commented Jul 25, 2018 at 14:54
  • Then it looks fine, what don't you like about the solution ? Commented Jul 25, 2018 at 14:59
  • Possible duplicate of Typescript derive union type from tuple/array values Commented Jul 25, 2018 at 15:29

2 Answers 2

131

Since Typescript 3.4, you could use as const and generate a union type from an array as follows

const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] as const;
export type Color = typeof colors[number]; // 'red'|'orange'|'yellow'|'green'|'blue'|'indigo'|'violet'
Sign up to request clarification or add additional context in comments.

2 Comments

What is the magic number thing which converts it to 'something' | .. from ['something'] ?
Thats a good question. Basically you are creating a type Color which has the type of the array colors accessed at any index. Since number can take any numeric value, the type created is a union type of all the possible accessed entries of the array ('red'|'orange'|'yellow'|'green'|'blue'|'indigo'|'violet')
3

Building on @guido-dizioli's answer, you can create a reusable function to create a type dynamically and still benefit from IntelliSense:

enter image description here One catch is that the caller needs to define the array with as const:

const properties = [
    "prepare", 
    "loadSession", 
    "proceedBasedOnSession", 
    "fetchConnection"
] as const;

function createObject<T extends readonly string[]>(steps: T): Record<T[number], object> {
    const typed = {} as Record<string, string>;
    steps.forEach(step => typed[step] = step);
    return typed as unknown as Record<T[number], object>;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.