How to check if a given string key exists in Enum

You could use the in operator:

if ('value4' in someEnum) {
  // ...
}

For TypeScript 3.7 with a target of es2017 or higher

enum EList {
  ITEM_FOO = 'fooData',
  ITEM_BAR = 'barData'
}

const lookingForKey = 'ITEM_BAR'
const lookingForValue = 'barData'

// test if `lookingForKey` exists within `EList`
console.log(Object.keys(EList).some((v) => v === lookingForKey))

// test if `lookingForValue` exists within `EList`
console.log(Object.values(EList).some((v) => v === lookingForValue))

To understand how to check if a value exists in enum, one has to understand what it becomes after compilation. And it is nothing more than a good old JavaScript object populated by an IIFE (Immediately Invoked Function Expression).

Let us assume you have a simple string-based enum:

enum Test {
  TEST = "test"
}

This is how the compiled JavaScript code looks like:

var Test;
(function (Test) {
    Test["TEST"] = "test";
})(Test || (Test = {}));

Note that the result of this is simply:

var Test = {
  "TEST": "test"
}

It means that, obviously, the in operator is enough to check if a key is present in the enum. It also is enough in case the value is equal to the key, but only accidentally.

Type guard is indeed a more viable solution, but it could also be improved by:

  1. Optimizing a little - a simple for...in loop will do, in this case, we can even omit the hasOwnProperty guard.
  2. Making the type guard generic for reusability.

Note that val param is not simply T, but is a value in T (hence T[keyof T]):

function hasA<T>(obj : T, val: any) : val is T[keyof T] {
    
    for(const k in obj) {
        if( obj[k] === val ) {
            return true;
        }
    }
    
    return false;
};

Testing to make sure everything works:

var t = "something";

if(hasA(Test,t)) {
    t //inferred as "Test"
}

var d = "any";

if( !hasA(Test, d) ) {
    d //inferred as "string"
}

console.log( hasA( Test, "something" ) ); //true
console.log( hasA(Test, "anything") ); //false

Typescript enums are a little annoying because they have two sides - the atom used to name a value, and the actual value. These each have a different way to check them.

Let's use an example enum that represents actions a content blocker can take. We'll get a kebab-case value from our API but want to use a camelCase value in TS:

enum ActionType {
  block = "block",
  cssDisplayNone = "css-display-none",
  ignorePreviousRules = "ignore-previous-rules"
}

Now if we wanted to check if it was valid to say ActionType.cssDisplayNone in our code, we can check that with the in operator. However, if we have a value from an API and we want to see if the value we got is an ActionType, that won't work!

const canBlockCss = 'cssDisplayNone' in ActionType; // Returns true
const isValidAction = 'css-display-none' in ActionType; // Returns false!

In this case, we need to write a type guard:

function isActionType(test: any): test is ActionType {
    return (Object.values(ActionType).indexOf(test) !== -1);
}

const isValidAction = isActionType('css-display-none') // Returns true

This has the added bonus that if you have a variable of unknown type and pass it to a type guard, the return value will be included in Typescript's understanding of the variable, allowing you to cast it at the same time as you check it.

Tags:

Typescript