简体   繁体   中英

TypeScript mapping enum

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

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

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.

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

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM