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:
- Optimizing a little - a simple
for...in
loop will do, in this case, we can even omit thehasOwnProperty
guard. - 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.