9

I found this great answer about how to convert a string to a typescript enum. Based on that I have written this function

enum Color { Red='red', Green='green' }

function mapColorString(strColor: string): Color {
  const colorKey = strColor as keyof typeof Color
  return Color[colorKey]
}

But now when I try to make it generic,

function getEnumFromString<T>(str: string): T {
  const enumKey = str as keyof T
  return T[enumKey]
}

I get the error in the return statement: 'T' only refers to a type, but is being used as a value here.

I want to make this generic because I have a number of enums that I need to generate based on their string values, and I would like to not have a separate method for every one.

Zach Olivare
  • 3,805
  • 3
  • 32
  • 45

4 Answers4

14

I can get this to work when i pass the enum definition:

enum Color { Red='red', Green='green' }

function getEnumFromString<T>(type: T, str: string): T[keyof T] {
    const casted = str as keyof T;
    return type[casted];
}

const bar = getEnumFromString(Color, 'Red');
Tom Cumming
  • 1,078
  • 7
  • 9
4

T is just going to be the type of the enum. Types are erased and don't exist at runtime. You need to pass in the object representing the enum:

enum Color { Red='red', Green='green' }

function getEnumFromString<T, K extends string>(enumObj: { [P in K]: T },str: string): T {
    const enumKey = str as K
    return enumObj[enumKey]
}
getEnumFromString(Color, 'Red');

K will represent the keys of the enum, T will be the type for the enum value

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • Thank you for your quick response! But can you please explain how `{ [P in K]: T }` equates to an `enum` type? – Zach Olivare Oct 25 '18 at 15:15
  • @ZachPosten It will equate to a type with keys and values (so basically any object). `T` will contain the type of values of that type. For an enum object that will be the enum value type. There is no way to make 100% sure the object will be an enum, this just happens to work for enums. (The other answer has the same limitation) – Titian Cernicova-Dragomir Oct 25 '18 at 15:17
0

This solution seems to work well for me :

  /**
   * Converts a string value to an enum entry
   * @param obj the enum class
   * @param str string to convert
   * @return enum value or undefined
   */
  static stringToEnum<T>(obj: T, str: string) : T[keyof T]
  {
    return Object.values(obj).includes(str as keyof T) ? str as unknown as T[keyof T] : undefined;
  }

Different implementation using native casting, passing through unknown feels jerky though.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Sebus
  • 1
  • 1
0

I can create a more generic with ignoring the case.

enum Color { Red='red', Green='green',Test=1,Test2 }

function getEnumFromString<T>(type: T ,str: string): any {
   const enumName = (Object.keys(type) as Array<keyof T>).find(k =>(type as any)[k].toLowerCase() === str.toLowerCase()) as keyof T;
    var keyValue=isNaN(Number(enumName))? enumName:Number(enumName);
    return keyValue;
}
getEnumFromString(Color, 'Test');// output: 1
getEnumFromString(Color, 'Test2');// output: 2
getEnumFromString(Color, 'rEd');//output Red