7

I'd like to write something like this in Typescript:

export function stringToEnum<T>(enumObj: T, str: string): keyof T {
    return enumObj[str];
}

and use it as follows:

enum MyEnum {
  Foo
}

stringToEnum<MyEnum>(MyEnum, 'Foo');

where it would return

MyEnum.Foo

The function above works as expected... but the typings are throwing errors. For the parameter MyEnum in stringToEnum<MyEnum>(MyEnum, 'Foo');, Typescript complains tha:

Argument of type 'typeof MyEnum' is not assignable to parameter of type 'MyEnum'

which makes sense... unfortunately. Any ideas on how I can get around this?

1
  • Why? Whats your usecase? Commented Jan 25, 2018 at 19:25

3 Answers 3

11

You can do it all natively without having to write a function:

enum Color {
    red,
    green,
    blue
}

// Enum to string
const redString: string = Color[Color.red];
alert(redString);

// String to enum
const str = 'red';
const redEnum: Color = Color[str];
alert(redEnum);

Or you can have some fun with it...

enum MyEnum {
  Foo,
  Bar
}

function stringToEnum<ET, T>(enumObj: ET, str: keyof ET): T{
    return enumObj[<string>str];
}

const val = stringToEnum<typeof MyEnum, MyEnum>(MyEnum, 'Foo');

// Detects that `foo` is a typo
const val2 = stringToEnum<typeof MyEnum, MyEnum>(MyEnum, 'foo');
Sign up to request clarification or add additional context in comments.

4 Comments

Right. Is there any way to do it with a method, though?
I've added an example for fun... the call to the function is significantly trickier than just using MyEnum['Foo'].
Hey @Fenton. TS compiler errors out with 7053 in current TS versions, also within your function. This explains it. So the easiest solution would be: const str: string = 'red'; const redEnum: Color = (<any>Color)[str];
In addition to my last comment: If you got an enum like enum Color { Red = "red"} then const redEnum: Color = (<any>Color)["red"] won't match as the indexer matches the enum-member-name. So only const redEnum: Color = (<any>Color)["Red"] would work.
3

Your signature is a bit mixed up. The return type should be T[keyof T] if you intend for the method to return an enum value. The type of the str param should also be keyof T to prevent you from passing invalid strings in, but this will limit you to passing string literals in (or well-typed variables of type keyof T, but not string):

function stringToEnum<T>(enumObj: T, str: keyof T): T[keyof T]

Then either don't specify the type param and let the compiler infer the type correctly:

// type: Foo
// value: 0  
const result = stringToEnum(MyEnum, 'Foo');

Or you need to provide typeof MyEnum as the type param:

// type: Foo
// value: 0  
const result = stringToEnum<typeof MyEnum>(MyEnum, 'Foo');

If you really want to be able to pass in any arbitrary string enum name, then the return type is a lie: should be T[keyof T] | undefined. You'll also run into trouble when attempting enumObj[str] if the type of str is string and you have noImplicitAny compiler option enabled.

There's a bit more to making generic functions that work with enum types properly, especially numeric enums that have reverse lookup entries at run-time. Take a look at the source code for ts-enum-util (github, npm) for inspiration

Comments

1
 stringToEnum(MyEnum, 'Foo');

Just leave away the generic and let typescript do that. Thats because the type stored under MyEnum does not match the Enum itself but is a union type of its values:

 enum Test { A, B };

 const value: Test /* "A" | "B" */ = Test.A;

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.