John White John White - 3 months ago 21
TypeScript Question

Get enum item type from enum type

Given the following

enum
:

enum Style {
Compact,
Switch,
Default
}


how can I get the type
Style
from the type
typeof Style
? For example, I would like to use it with my enum-cast function, which takes in an enum definition and a number as its arguments, and returns the number "as an item" of the specified enum if that number is defined by the specified enum:

function castToEnum<T>(_enum: T, value: number): T {
if (_enum.hasOwnProperty(value.toString())) {
return value as any; // 'value' is defined by '_enum'.
} else {
throw new TypeError(`enum does not define value '${value}'.`);
}
}


This is how I would like it to be working:

var style1 = castToEnum(Style, 0); // Ok, style1 === Style.Compact
var style2 = castToEnum(Style, 1); // Ok, style2 === Style.Switch
var style3 = castToEnum(Style, 2); // Ok, style3 === Style.Default
var style4 = castToEnum(Style, 3); // TypeError


Here, I would like
styleX
to be of type
Style
. However, since the generic type parameter
T
on
castToEnum
is detected as having the type of
typeof Style
,
styleX
will be of that type as well.

Demo on Playground

How could I accomplish this? I tried using
typeof
, but it does not work with generic type parameters, since those are types already.

How can I make this type conversion, from an enum type to an enum item type?




Edit:

With non-abstract classes, it is possible to get the instance type from a class type with the following function signature:

function createInstance<T>(_class: new () => T): T {
return new _class();
}


This will effectively take an argument of type
typeof T
, and return a value of type
T
, in case of classes with a parameterless constructor signature. This approach does not work for enums, unfortunately.

Answer

You need to receive the enum as any:

function castToEnum<T>(_enum: any, ordinal: number): T {
    if (_enum.hasOwnProperty(ordinal)) {
        return _enum[ordinal];
    } else {
        throw new TypeError(`enum does not define value '${ ordinal }'.`);
    }
}

console.log(castToEnum(Style, 0)); // Compact
console.log(castToEnum(Style, 1)); // Switch
console.log(castToEnum(Style, 2)); // Default
console.log(castToEnum(Style, 3)); // TypeError

(code in playground)


Edit

This should work as you want it to:

function castToEnum(_enum: any, ordinal: number): number {
    if (_enum[ordinal]) {
        return ordinal;
    } else {
        throw new TypeError(`enum does not define value '${ ordinal }'.`);
    }
}

var style: Style = castToEnum(Style, 1);
console.log(style === Style.Switch);
console.log(castToEnum(Style, 3)); // TypeError

(code in playground)

The function needs to return a number otherwise this:

style === Style.Switch

Will be false.
And it works with type safety.