What are enum Flags in TypeScript?

Typescript

Typescript Problem Overview


I'm learning TypeScript using this ebook as a reference. I've checked the TypeScript Official Documentation but I don't find information about enum flags.

Typescript Solutions


Solution 1 - Typescript

They're a way to efficiently store and represent a collection of boolean values.

For example, taking this flags enum:

enum Traits {
    None = 0,
    Friendly = 1 << 0, // 0001 -- the bitshift is unnecessary, but done for consistency
    Mean = 1 << 1,     // 0010
    Funny = 1 << 2,    // 0100
    Boring = 1 << 3,   // 1000
    All = ~(~0 << 4)   // 1111
}

Instead of only being able to represent a single value like so:

let traits = Traits.Mean;

We can represent multiple values in a single variable:

let traits = Traits.Mean | Traits.Funny; // (0010 | 0100) === 0110

Then test for them individually:

if ((traits & Traits.Mean) === Traits.Mean) {
    console.log(":(");
}

Solution 2 - Typescript

The official documentation has this example that I will add some details that are crucial to use enum and flags.

enum FileAccess {
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
}

In TypeScript, you can assign a value directly with =

let x:FileAccess = FileAccess.Read;

But this might override previous values. To get around that you can use |= to append a flag.

x |= FileAccess.Write;

At this point, the variable x is Read and Write. You can remove a value by using the ampersand and tilde:

x &= ~FileAccess.Read;

Finally, you can compare to see if one of the value is set to the variable. The accepted answer is not right. It should not just use the ampersand symbol but also check with === to the desired value. The reason is the ampersand returns a number, not a boolean.

console.log(FileAccess.Write === (x & FileAccess.Write)); // Return true
console.log(FileAccess.Read === (x & FileAccess.Read)); // Return false

Solution 3 - Typescript

You can check this solution, that add some infra, but if you will use it a lot it really nice, the usage goes like that:

let FlaggedExample: IFlaggedEnum = FlaggedEnum.create(Example, 1 << 2);  // class definition

let example = new FlaggedExample(3); // Alpha,Beta instance of the class
export module FlaggedEnum {
    "use strict";
    export interface IFlaggedEnumGenerator {
        (_enum: any, _max: number): IFlaggedEnum;
    }

    export interface IFlaggedEnum {
        (val: IFlaggedEnum): void;
        (val: number): void;
        (val: string): void;

        /** array of the individual enum flags that represent the value 
         */
        toArray(): IFlaggedEnum[];

        /** does this instance contain all the flags of the value 
         */
        contains(val: IFlaggedEnum): boolean;
        contains(val: number): boolean;
        contains(val: string): boolean;

        /** adds the flags to the value and returns a new instance 
         */
        add(val: IFlaggedEnum): IFlaggedEnum;
        add(val: number): IFlaggedEnum;
        add(val: string): IFlaggedEnum;

        /** removes the flags from the value and returns a new instance 
         */
        remove(val: IFlaggedEnum): IFlaggedEnum;
        remove(val: number): IFlaggedEnum;
        remove(val: string): IFlaggedEnum;

        /** returns an instance containing all intersecting flags 
         */
        intersect(val: IFlaggedEnum): IFlaggedEnum;
        intersect(val: number): IFlaggedEnum;
        intersect(val: string): IFlaggedEnum;

        /** does the two instances equal each other 
         */
        equals(val: IFlaggedEnum): boolean;
        equals(val: number): boolean;
        equals(val: string): boolean;

    }

    /** create a class definition for a Flagged Enum
     * @method create
     * @param _enum {enum} The enum definition being exteded
     * @param _max {number} the maximum possible value of the enum being extended
     * @returns {IFlaggedEnum} the class definition for the provided enum
     */
    export var create: IFlaggedEnumGenerator = function (_enum: any, _max: number): IFlaggedEnum {

        var base: any = _enum,
            max: number = _max;

        var Base: IFlaggedEnum = <any>function (val: any): void {
            if (typeof (val) === "string") {
                val = base[val];
            }
            this.value = val + 0;
        };

        var proto: any = Base.prototype;

        proto.valueOf = function (): number { return <number>this.value; };
        proto.toString = function (): string {
            var list: string[] = [];
            for (var i: number = 1; i < max; i = i << 1) {
                if ((this.value & i) !== 0) {
                    list.push(base[i]);
                }
            }
            return list.toString();
        };

        proto.toArray = function (): IFlaggedEnum[] {
            var list: IFlaggedEnum[] = [];
            for (var i: number = 1; i < max; i = i << 1) {
                if ((this.value & i) !== 0) {
                    list.push(new Base(i));
                }
            }
            return list;
        };

        proto.contains = function (val: any): boolean {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return (this.value & val) === (val + 0);
        };

        proto.add = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return new Base(this.value | val);
        };

        proto.remove = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = this.base[val];
            }
            return new Base((this.value ^ val) & this.value);
        };

        proto.intersect = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = base[val];
            }
            var final: number = 0;
            for (var i: number = 1; i < max; i = (i << 1)) {
                if ((this.value & i) !== 0 && (val & i) !== 0) {
                    final += i;
                }
            }
            return new Base(final);
        };

        proto.equals = function (val: any): boolean {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return this.value === (val + 0);
        };

        return Base;

    };
}

Solution 4 - Typescript

You can just use the bit values do define your enum

enum FileAccess {
    None    = 0,
    Read    = 1,
    Write   = 2,
    Active  = 4,
    Failed  = 8
}

Solution 5 - Typescript

Flags allow you to check if a certain condition from a set of conditions is true. This is a common programming pattern in various other programming languages e.g. here is an example about C# : https://stackoverflow.com/questions/530077/using-bitwise-operators-on-flags

Solution 6 - Typescript

enum Info{
    None = 0,
    glass= 1 << 0, // 0001 -- the bitshift is unnecessary, but done for consistency
    plastic= 1 << 1,     // 0010
   
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJaime RiosView Question on Stackoverflow
Solution 1 - TypescriptDavid SherretView Answer on Stackoverflow
Solution 2 - TypescriptPatrick DesjardinsView Answer on Stackoverflow
Solution 3 - TypescriptOded BDView Answer on Stackoverflow
Solution 4 - TypescriptBrainbustedView Answer on Stackoverflow
Solution 5 - TypescriptbasaratView Answer on Stackoverflow
Solution 6 - Typescriptsnehal badheView Answer on Stackoverflow