How to check if a given string key exists in Enum

后端 未结 4 1851
一生所求
一生所求 2020-12-16 12:08

I have an enum defined like this

export enum someEnum {
    None = \'\',
    value1 = \'value1\',
    value2 = \'value2\',
         


        
相关标签:
4条回答
  • 2020-12-16 12:42

    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.

    0 讨论(0)
  • 2020-12-16 12:54

    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.

    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 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
    
    0 讨论(0)
  • 2020-12-16 12:57

    You could use the in operator:

    if ('value4' in someEnum) {
      // ...
    }
    
    0 讨论(0)
  • 2020-12-16 12:59

    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))
    
    0 讨论(0)
提交回复
热议问题