0

In my application we work on mix of mocks and real REST data. In TypeScript i have whole bunch of enumse defined for convenience.

When I create any kind of mock array with data, I use following constriction:

enum MyEnum { 'myEnumValue1' = 0, myEnumValue2 } 
(...)
 enumField: MyEnum.myEnumValue1,
(...)

which is effectively resolved by TypeScript to number:

(...)
enumField: 1,
(...)

However, form my REST API I'm receiving same set of enums as their string representation. Conversion in both way is possible via:

MyEnum['string'] => number
MyEnum[number] => string

MyEnum['myEnumValue1'] => 0
MyEnum[0] => 'myEnumValue1'

Is it possible to generate generic class that will handle this conversion in graceful way, similar to how Stack Community suggested me in THIS question

4 Answers 4

2

You can create a function similar to the one for objects:

// Return type is a bit more tricky because we have to get the enum type from typeof enum
function fromValue<T>(o: T, value: string): { [P in keyof T]: T[P]  }[keyof T]{
    return  (o as any)[value]; // No type safety here unfrotunately
}

var value = fromValue(MyEnum, ""); //value will be of type MyEnum
Sign up to request clarification or add additional context in comments.

1 Comment

Well... it's definitely not similar ;) works like a charm. I have question, what does the part [keyof T] stands for? Beside that i see that keyof was a... well key to solution. Thank you once again!
2

Beside perfect answer from Titan, here is a little tweak to work both ways regarding what type of value you wish to map from/to (string or number) and what unified result you wish (string or number):

enum MyEnum {
    'VAL_ONE' = 0,
    'VAL_TWO' = 1
}

function fromValuetoNumber<T>(o: T, value: string | number): {[P in keyof T]: T[P]} {
    if (typeof (value) === 'string') {
        return  (o as T)[value]; 
    } else if (typeof (value) === 'number') {
        return (o as T)[o[value]]
    }   
}

function fromValueToString<T>(o: T, value: string | number): {[P in keyof T]: T[P]} {
    if (typeof (value) === 'string') {
        return  (o as T)[o[value]]; 
    } else if (typeof (value) === 'number') {
        return (o as T)[value]
    }   
}

console.log(fromValuetoNumber(MyEnum, 'VAL_ONE'))
console.log(fromValuetoNumber(MyEnum, 0))

console.log(fromValueToString(MyEnum, 'VAL_ONE'))
console.log(fromValueToString(MyEnum, 0))

Only thing that still bothers me is fact, that if generic type will be assigned, TypeScript goes crazy:

fromValueToString<MyEnum>(MyEnum, 'VAL_ONE')

Still, this is just an addition to original answer.

4 Comments

try fromValueToString<typeof MyEnum>(MyEnum, 'VAL_ONE')
Yeah, but this will not gain us much more type safety, since typeof(AnyEnum) is just an object, is that correct?
no it will not that is true, but what extra safety would you want maybe we can achieve it :)
Well I assume that validation of parameter value correctness is rather technical impossible (i.e. verifying that string in fact is keyof MyEnum or number does have matching pair in enum set). It's just a bit confusing, that TypeScript don't treat enums as classes with dedicated type, rather than plain objects... Anyway, it's still cool :)
0

How about using string literals instead of enums?

I'm guessing that you are using the enums to avoid typos, and you want TypeScript to "catch" it for you. So if you use string literals, you are type safe and you will talk to the server in the same "language"

For example:

export type Sex = 'male' | 'female';
var sex: Sex = 'mela'; // Typo => ERROR from typescript

1 Comment

I'm afraid such easy transition is not possible, because we use them in one additional major use case, array indexation. In this case changing them to string literals (which in general i agree, would be beneficial from sake of simplicity) would make our life much harder
0

Using ts-enum-util (github, npm), you can perform type-safe conversions between enum values and names (in either direction) with run-time validation. You can choose between variations of method that either throw an error or return a default value (undefined by default) if an invalid value is encountered at run time.

Example:

import {$enum} from "ts-enum-util";

enum MyEnum {
    FOO = 0,
    BAR = 1
}

function getMyEnum1(apiString: string): MyEnum {
    // throws informative error if "apiString" is not 
    // "FOO" or "BAR"
    return $enum(MyEnum).getValueOrThrow(apiString);
}

function getMyEnum2(apiString: string): MyEnum {
    // returns MyEnum.FOO if "apiString" is not 
    // "FOO" or "BAR"
    return $enum(MyEnum).getValueOrDefault(apiString, MyEnum.FOO);
}

function getMyEnum3(apiString: string): MyEnum | undefined {
    // returns undefined if "apiString" is not 
    // "FOO" or "BAR"
    return $enum(MyEnum).getValueOrDefault(apiString);
}

// type: ("FOO" | "BAR")
// value: "BAR"
const enumName = $enum(MyEnum).getKeyOrThrow(1);

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.