Skip to content

A implementation of enum like feature, but more powerful and extensible.

License

Notifications You must be signed in to change notification settings

chengjianhua/rich-enum

Repository files navigation

rich-enum

A implementation of enum like feature, but more powerful and extensible.


Build Status Code Coverage version downloads MIT License

All Contributors PRs Welcome Code of Conduct

Watch on GitHub Star on GitHub Tweet

The problem

We could use a plain object to simulate an enumeration type. It's fine that if we just need to use this to store the value of a key and access the predefined value by a key. What if we need to store and read more informations by a key or read some information by value ?

Take the api documentation described by protocol buffers as an example:

message SearchRequest {
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }

  Corpus corpus = 4;
}

In the above example, it define an value corpus of enum type Corpus. When we communicate with the back end api, wo need to passed something like this:

const Corpus = {
  UNIVERSAL: 0,
  WEB: 1,
  IMAGES: 2,
  LOCAL: 3,
  NEWS: 4,
  PRODUCTS: 5,
  VIDEO: 6,
}

const request = {
  corpus: Corpus.UNIVERSAL,
}

In the context of communicating with the back end API, we can create an object to map the key like UNIVERSAL to the value 0 and other enumeration values the same way. But if we want to add more information to the enumeration value? Such as detailed description and any other extra pieces of information. For example, we have a value 0 and we want to know which enumeration item has the value 0, we also want to display the corresponding description for this value.

const CorpusText = {
  [Corpus.UNIVERSAL]: 'Universal',
  //...
  [Corpus.VIDEO]: 'Video',
}

CorpusText[corpusValue]

The solution the above provided can meet our needs, but it's too troublesome. So we take rich-enum as the more great solution.

This solution

This solution is apparent when we define the enum value, we attach more information with it, then through the key and extensible API to use the enumeration value in the form we need.

Table of Contents

Installation

This module is distributed via npm which is bundled with node and should be installed as one of your project's dependencies:

npm install --save rich-enum

Usage

Basic

Define a powerful enumeration:

import RichEnum from 'rich-enum'

const CorpusEnum = new RichEnum({
  UNIVERSAL: {
    value: 0,
    text: 'Universal',
  },
  WEB: {
    value: 1,
    text: 'Web',
  },
  IMAGES: {
    value: 2,
    text: 'Images',
  },
  LOCAL: {
    value: 3,
    text: 'Local',
  },
  NEWS: {
    value: 4,
    text: 'News',
  },
  PRODUCTS: {
    value: 5,
    text: 'Products',
  },
  VIDEO: {
    value: 6,
    text: 'Video',
  },
})

Then use this:

const {value: Corpus} = CorpusEnum
/* read the value by key */
Corpus.UNIVERSAL // 0
Corpus.VIDEO // 6

/* read the text by value */
CorpusEnum.text[Corpus.UNIVERSAL] // 'Universal'
CorpusEnum.text[Corpus.VIDEO] // 'Video'

/* read the all details of one element */
CorpusEnum.UNIVERSAL // { key: 'UNIVERSAL', value: 0, text: 'UNIVERSAL' }
CorpusEnum.VIDEO // { key: 'VIDEO', value: 6, text: 'Video' }

/* loop the all enumerations with detailed information */
CorpusEnum.collection.forEach(({key, value, text}) => {
  console.log({key, value, text})
  // { key: 'UNIVERSAL', value: 0, text: 'Universal' }
  // ...
  // { key: 'VIDEO', value: 6, text: 'Video' }
})

The RichEnum instance has properties whose key is the value of an enumeration item, and the value is the property value of the enumeration item for every property defined for enumeration items except property value.

The property value is the object whose key is the key of an item, and value is the value of property value in the properties.

For Example:

const enumeration = new RichEnumeration({
  TYPE_A: {
    value: 0,
    text: 'Type A',
    extra: 'Extra info',
    xxx: 'xxx',
  },
})

enumeration.value.TYPE_A // 0
enumeration.text[0] // 'Type A'
enumeration.extra[0] // 'Extra info'
enumeration.xxx[0] // 'xxx'

extend()

if you defined a rich enumeration object, it's fine for your most scenes to be used. What if you have some extra information need to be bound with the same key only for a specified scene ?

You're allowed to extend it to generate a new RichEnum instance object, for example:

const SpecifiedCorpusEnum = CorpusEnum.extend({
  UNIVERSAL: {
    extra: 'Extra information of the universal',
  },
})

CorpusEnum.UNIVERSAL // { key: 'UNIVERSAL', value: 0, text: 'Universal', extra: 'Extra information of the universal' }
CorpusEnum.UNIVERSAL.extra // 'Extra information of the universal'

The instance method new RichEnum().extend() will merge the new detail object with the older one shallowly per key. And return a new instance, so it will not pollute the original instance.

Other Solutions

I'm not aware of any, if you are please make a pull request and add it here!

Contributors

Thanks goes to these people (emoji key):


Jianhua Cheng

💻 📖 🚇 ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT