Tim Schoch
Tim Schoch

Reputation: 6434

Check if value exists in enum in TypeScript

I receive a number type = 3 and have to check if it exists in this enum:

export const MESSAGE_TYPE = {
    INFO: 1,
    SUCCESS: 2,
    WARNING: 3,
    ERROR: 4,
};

The best way I found is by getting all Enum Values as an array and using indexOf on it. But the resulting code isn't very legible:

if( -1 < _.values( MESSAGE_TYPE ).indexOf( _.toInteger( type ) ) ) {
    // do stuff ...
}

Is there a simpler way of doing this?

Upvotes: 539

Views: 574853

Answers (15)

husayt
husayt

Reputation: 15139

Here is solution which works for both numeric and string enums:

enum Es {
  A = "a",
  B = "b",
  C = "c",
}

enum En {
  A,
  B,
  C,
}

enum Ep {
  A,
  B = 2,
  C = 5,
}


console.log(enumValues(Es).includes("a")); // true
console.log(enumValues(En).includes(1)); // true
console.log(enumValues(Ep).includes(2)); // true

where

function enumValues<T extends object>(e: T) {
  const values = Object.values(e)
  const isNumEnum = e[e[values[0]]] === values[0]
  return isNumEnum ? values.slice(values.length / 2) : values
}

Try it in TS Playground

Upvotes: 0

Arst
Arst

Reputation: 3297

I made a handy function that can be used by an enum type. Check this link. It is also type predicate.

function isEnum<EV, ET extends { [key: string]: EV }>(enumValue: EV, enumType: ET): enumValue is ET[keyof ET] {
  return Object.values(enumType).includes(enumValue);
}

Examples:

enum MyFavoriteColor {
  RED = '#FF0000',
  GREEN = '#00FF00',
  BLUE = '#0000FF',
}

const color1 = MyFavoriteColor.RED
if (isEnum(color1, MyFavoriteColor)) {
  console.log(`color '${color1}' is MyFavoriteColor`)
} else {
  console.log(`color '${color1}' is not MyFavoriteColor`)
}

const color2 = '#FF0000'
if (isEnum(color2, MyFavoriteColor)) {
  console.log(`color '${color2}' is MyFavoriteColor`)
} else {
  console.log(`color '${color2}' is not MyFavoriteColor`)
}

const color3 = 'RED'
if (isEnum(color3, MyFavoriteColor)) {
  console.log(`color '${color3}' is MyFavoriteColor`)
} else {
  console.log(`color '${color3}' is not MyFavoriteColor`)
}

Output:

[LOG]: "color '#FF0000' is MyFavoriteColor" 
[LOG]: "color '#FF0000' is MyFavoriteColor" 
[LOG]: "color 'RED' is not MyFavoriteColor"  

Upvotes: 1

Shamsul Arefin
Shamsul Arefin

Reputation: 1917

For my case, this worked. I had to typecast my string to Enum.

export declare enum UserType {
    REGULAR_USER = "regular",
    ADMIN_USER = "admin",
}
const type = "admin"
if(Object.values(UserType).includes(type as UserType))
  console.log('You are admin')

Upvotes: -1

Xiv
Xiv

Reputation: 10043

If you want this to work with string enums, you need to use Object.values(ENUM).includes(ENUM.value) because string enums are not reverse mapped, according to https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html#string-enums:

enum Vehicle {
    Car = 'car',
    Bike = 'bike',
    Truck = 'truck'
}

becomes:

{
    Car: 'car',
    Bike: 'bike',
    Truck: 'truck'
}

So you just need to do:

if (Object.values(Vehicle).includes('car')) {
    // Do stuff here
}

If you get an error for: Property 'values' does not exist on type 'ObjectConstructor', then you are not targeting ES2017. You can either use this tsconfig.json config:

"compilerOptions": {
    "lib": ["es2017"]
}

Or you can just do an any cast:

if ((<any>Object).values(Vehicle).includes('car')) {
    // Do stuff here
}

Upvotes: 671

vinczemarton
vinczemarton

Reputation: 8156

According to sandersn the best way to do this would be:

Object.values(MESSAGE_TYPE).includes(type as MESSAGE_TYPE)

Upvotes: 162

Saravana
Saravana

Reputation: 40534

This works only on non-const, number-based enums. For const enums or enums of other types, see this answer


If you are using TypeScript, you can use an actual enum. Then you can check it using in.

export enum MESSAGE_TYPE {
    INFO = 1,
    SUCCESS = 2,
    WARNING = 3,
    ERROR = 4,
};

var type = 3;

if (type in MESSAGE_TYPE) {

}

This works because when you compile the above enum, it generates the below object:

{
    '1': 'INFO',
    '2': 'SUCCESS',
    '3': 'WARNING',
    '4': 'ERROR',
    INFO: 1,
    SUCCESS: 2,
    WARNING: 3,
    ERROR: 4
}

Upvotes: 367

Mar Jayson San Agustin
Mar Jayson San Agustin

Reputation: 883

export enum YourEnum {
   enum1 = 'enum1',
   enum2 = 'enum2',
   enum3 = 'enum3',
}

const status = 'enumnumnum';

if (!Object.values(YourEnum)?.includes(status)) {
     throw new UnprocessableEntityResponse('Invalid enum val');
}

Upvotes: 41

DaveBagler
DaveBagler

Reputation: 369

Update:

I've found that whenever I need to check if a value exists in an enum, I don't really need an enum and that a type is a better solution. So my enum in my original answer becomes:

export type ValidColors =
  | "red"
  | "orange"
  | "yellow"
  | "green"
  | "blue"
  | "purple";

Original answer:

For clarity, I like to break the values and includes calls onto separate lines. Here's an example:

export enum ValidColors {
  Red = "red",
  Orange = "orange",
  Yellow = "yellow",
  Green = "green",
  Blue = "blue",
  Purple = "purple",
}

function isValidColor(color: string): boolean {
  const options: string[] = Object.values(ValidColors);
  return options.includes(color);
}

Upvotes: 4

Dyllon Gagnier
Dyllon Gagnier

Reputation: 381

The following function returns another function which acts as a type predicate for the input enum (assuming it is a string style enum).

function constructEnumPredicate<RuntimeT extends string, EnumClass extends {[key: string]: RuntimeT}>(enumClass: EnumClass): (maybeEnum: string) => maybeEnum is EnumClass[keyof EnumClass] {
    const reverseMapping: {[key: string]: boolean} = {};

    for (const enumVal in enumClass) {
        const enumStr = enumClass[enumVal];
        reverseMapping[enumStr] = true;
    }

    function result(maybeEnum: any): maybeEnum is EnumClass[keyof EnumClass] {
        return !!reverseMapping[maybeEnum];
    }

    return result;
}

It works in TypeScript 4.2.4, but I have not tested earlier versions.

The main interesting part is the EnumClass[keyof EnumClass] return type. When such a type is an enum in TypeScript, it returns the original type of the enum where EnumClass is the type of the runtime enum class.

For an example of how to use this construction, suppose we have the following enum:

enum Direction {
    Left = "<-",
    Right = "->"
}

Direction is both a type as well as a runtime object. We can generate a type predicate for Direction and use it like so:

const isDirection = constructEnumPredicate(Direction);
function coerceDirection(maybeDir: string): Direction {
    // Since we make a type predicate rather than just a normal predicate,
    // no explicit type casting is necessary!
    return isDirection(maybeDir) ? maybeDir : Direction.Left;
}

Upvotes: 1

gdbdable
gdbdable

Reputation: 4501

If you there to find how to check union contain specific value, there is solution:

// source enum type
export const EMessagaType = {
   Info,
   Success,
   Warning,
   Error,
};

//check helper
const isUnionHasValue = <T extends number>(union: T, value: T) =>
   (union & value) === value;


//tests
console.log(
 isUnionHasValue(EMessagaType.Info | EMessagaType.Success), 
 EMessagaType.Success);

//output: true


console.log(
 isUnionHasValue(EMessagaType.Info | EMessagaType.Success), 
 EMessagaType.Error); 

//output: false

Upvotes: 0

gqstav
gqstav

Reputation: 2072

Type assertion is un-avoidable. Following up on

enum Vehicle {
    Car = 'car',
    Bike = 'bike',
    Truck = 'truck'
}

I found one alternative that wasn't mentioned so thought I'd share my fix for it:

const someString: Vehicle | string = 'car';
const inEnum = (Object.values(Vehicle) as string[]).includes(someString);

I find this more truthful because we usually come in typesafe(with a string) and want to compare it to the enum; it'd be a bit reckless to typecast it to any(reason: never do this) or Vehicle(reason: likely untruthful). Instead, typecasting the Object.values() output to an array of strings is in-fact very much real.

Upvotes: 19

waternova
waternova

Reputation: 1568

For anyone who comes here looking to validate if a string is one of the values of an enum and type convert it, I wrote this function that returns the proper type and returns undefined if the string is not in the enum.

function keepIfInEnum<T>(
  value: string,
  enumObject: { [key: string]: T }
) {
  if (Object.values(enumObject).includes((value as unknown) as T)) {
    return (value as unknown) as T;
  } else {
    return undefined;
  }
}

As an example:

enum StringEnum {
  value1 = 'FirstValue',
  value2 = 'SecondValue',
}
keepIfInEnum<StringEnum>('FirstValue', StringEnum)  // 'FirstValue'
keepIfInEnum<StringEnum>('OtherValue', StringEnum)  // undefined

Upvotes: 4

raziEiL
raziEiL

Reputation: 87

enum ServicePlatform {
    UPLAY = "uplay",
    PSN = "psn",
    XBL = "xbl"
}

becomes:

{ UPLAY: 'uplay', PSN: 'psn', XBL: 'xbl' }

so

ServicePlatform.UPLAY in ServicePlatform // false

SOLUTION:

ServicePlatform.UPLAY.toUpperCase() in ServicePlatform // true

Upvotes: -2

Ester Kaufman
Ester Kaufman

Reputation: 868

There is a very simple and easy solution to your question:

var districtId = 210;

if (DistrictsEnum[districtId] != null) {

// Returns 'undefined' if the districtId not exists in the DistrictsEnum 
    model.handlingDistrictId = districtId;
}

Upvotes: 21

Nhan Cao
Nhan Cao

Reputation: 2515

export enum UserLevel {
  Staff = 0,
  Leader,
  Manager,
}

export enum Gender {
  None = "none",
  Male = "male",
  Female = "female",
}

Difference result in log:

log(Object.keys(Gender))
=>
[ 'None', 'Male', 'Female' ]

log(Object.keys(UserLevel))
=>
[ '0', '1', '2', 'Staff', 'Leader', 'Manager' ]

The solution, we need to remove key as a number.

export class Util {
  static existValueInEnum(type: any, value: any): boolean {
    return Object.keys(type).filter(k => isNaN(Number(k))).filter(k => type[k] === value).length > 0;
  }
}

Usage

// For string value
if (!Util.existValueInEnum(Gender, "XYZ")) {
  //todo
}

//For number value, remember cast to Number using Number(val)
if (!Util.existValueInEnum(UserLevel, 0)) {
  //todo
}

Upvotes: 10

Related Questions