ES6 read-only enums that can map value to name

前端 未结 3 626
长情又很酷
长情又很酷 2021-02-07 07:55

I would like to define an enum-like structure in JS, but have two requirements:

  1. The values be read-only, i.e. no users can assign to them.
  2. The values (0, 1
相关标签:
3条回答
  • 2021-02-07 08:19

    This does a pretty good job, IMHO.

    function Enum(a){
      let i = Object
        .keys(a)
        .reduce((o,k)=>(o[a[k]]=k,o),{});
    
      return Object.freeze(
        Object.keys(a).reduce(
          (o,k)=>(o[k]=a[k],o), v=>i[v]
        )
      );
    } // y u so terse?
    
    const FOO = Enum({
      a: 0,
      b: 1,
      c: "banana"
    });
    
    console.log(FOO.a, FOO.b, FOO.c);            // 0 1 banana
    console.log(FOO(0), FOO(1), FOO("banana"));  // a b c
    
    try {
      FOO.a = "nope";
    }
    catch (e){
      console.log(e);
    }
    
    0 讨论(0)
  • 2021-02-07 08:22

    Just recently implemented an Es6 version that works quite well:

    const k_VALUES = {}
    
    export class ErrorCode {
    
        constructor(p_apiCode, p_httpCode){
            this.apiCode = p_apiCode;
            this.httpCode = p_httpCode;
    
            k_VALUES[p_apiCode] = this;
        }
    
    
        static create(p_apiCode){
            if(k_VALUES[p_apiCode]){
                return k_VALUES[p_apiCode];
            }
    
            return ErrorCode.UNKNOWN;
        }
    }
    
    ErrorCode.UNKNOWN                 = new ErrorCode(0,     500);
    ErrorCode.NOT_FOUND               = new ErrorCode(-1000, 404);
    ErrorCode.NOT_FOUND_EMAIL         = new ErrorCode(-1001, 404);
    ErrorCode.BAD_REQUEST             = new ErrorCode(-1010, 404);
    

    I wanted to implement a similar pattern as what we do with Java enums. This enables me to use a constructor to pass values. The constructor then freezes the ErrorCode object - nice and convenient.

    Usage: first import your enum class...

    import {ErrorCode} from "../common/services/errors/ErrorCode";
    

    Now, after importing the enum class, access it like so:

    if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}
    

    PS> This is used in conjunction with a Webpack setup using Babel to convert our ES6 classes down for browser compatibility.

    0 讨论(0)
  • 2021-02-07 08:28

    I'd use a Map so that your enum values can be any type, rather than having them coerced into strings.

    function Enum(obj){
        const keysByValue = new Map();
        const EnumLookup = value => keysByValue.get(value);
    
        for (const key of Object.keys(obj)){
            EnumLookup[key] = obj[key];
            keysByValue.set(EnumLookup[key], key);
        }
    
        // Return a function with all your enum properties attached.
        // Calling the function with the value will return the key.
        return Object.freeze(EnumLookup);
    }
    

    If your enum is all strings, I'd also probably change one line to:

    EnumLookup[key] = Symbol(obj[key]);
    

    to ensure that the enum values are being used properly. Using just a string, you have no guarantee that some code hasn't simply passed a normal string that happens to be the same as one of your enum values. If your values are always strings or symbols, you could also swap out the Map for a simple object.

    0 讨论(0)
提交回复
热议问题