Enum flags in JavaScript

Viewed 47434

I need to emulate enum type in Javascript and approach seems pretty straight forward:

var MyEnum = {Left = 1; Right = 2; Top = 4; Bottom = 8}

Now, in C# I could combine those values like this:

MyEnum left_right = MyEnum.Left | MyEnum.Right

and then I can test if enum has certain value:

if (left_right & MyEnum.Left == MyEnum.Left) {...}

Can I do something like that in Javascript?

6 Answers

Flag Enumerations in JavaScript

Flag enums: Values must increment by powers of 2

var SEASONS = {
  Spring : 1,
  Summer : 2,
  Fall   : 4,
  Winter : 8
};

Cannot use 0 in a bitwise & operation to test for a flag b/c it will always result in zero. However, it can be used for logical comparisons.

Usage examples (contrived)

var getSeasonsSelected = function( seasons ) {
  var selected = [];

  // The perens are needed around the bitwise operation due to the
  // greater operator precedence of `===`
  if ( (seasons & SEASONS.Spring) === SEASONS.Spring ) selected.push('Spring');
  if ( (seasons & SEASONS.Summer) === SEASONS.Summer ) selected.push('Summer');
  if ( (seasons & SEASONS.Fall)   === SEASONS.Fall )   selected.push('Fall');
  if ( (seasons & SEASONS.Winter) === SEASONS.Winter ) selected.push('Winter');

  return selected;
};

var s1 = getSeasonsSelected( SEASONS.Spring | SEASONS.Fall );
console.log(s1);
//=> ["Spring", "Fall"]

There is my implementation in typescript:

export class FlagEnumService {

    constructor(private value: number = 0) {
    }

    public get() {
        return this.value;
    }

    public set(value: number): this {
        this.value = value;
        return this;
    }

    public has(key: number): boolean {
        return !!(this.value & key);
    }

    public add(key: number): this {
        this.value |= key;
        return this;
    }

    public delete(key: number): this {
        this.value &= ~key;
        return this;
    }

    public toggle(key: number): this {
        this.has(key) ? this.delete(key) : this.add(key);
        return this;
    }
}

And tests for clarification

import { FlagEnumService } from './flag-enum.service';

enum Test {
    None = 0,
    First = 1,
    Second = 2,
    Third = 4,
    Four = 8
}

describe('FlagEnumService', () => {
    let service: FlagEnumService;
    beforeEach(() => service = new FlagEnumService());

    it('should create with initial value', () => {
        service = new FlagEnumService(Test.First);
        expect(service.get()).toBe(Test.First);
    });

    it('should return true if has flag', () => {
        service = new FlagEnumService(Test.First);
        expect(service.has(Test.First)).toBe(true);
    });

    it('should return false if doesn\'t have flag', () => {
        service = new FlagEnumService(Test.First);
        expect(service.has(Test.Second)).toBe(false);
    });

    it('should add', () => {
        expect(service.add(Test.First).add(Test.Second).get()).toBe(Test.First + Test.Second);
    });

    it('should not add the same value twice', () => {
        expect(service.add(Test.First).add(Test.First).get()).toBe(Test.First);
    });

    it('should remove', () => {
        expect(
            service
                .add(Test.First)
                .add(Test.Second)
                .delete(Test.Second)
                .get()
        )
            .toBe(Test.First);
    });

    it('should return 0 when add then remove the same value', () => {
        expect(service.add(Test.First).delete(Test.First).get()).toBe(0);
    });

    it('should not remove not added values', () => {
        expect(service.add(Test.First).delete(Test.Second).get()).toBe(Test.First);
    });
});
Related