Home > Software design >  How to get all Enum Flags from a Number in TypeScript?
How to get all Enum Flags from a Number in TypeScript?

Time:10-06

Given:
Enum: {1, 4, 16}
Flags: 20

When: I pass Flags to a function

Then: I get array of flags of given Enum: [4, 16]

Note: I have tried to convert Enum to array manually and treat values as numbers. But I get overwhelmed when it gets to TS and I want to make that function dynamic in case Enum gets changed. I really don't want hardcode that. Please help!

enum Enum {
    one = 1,
    four = 4,
    sixteen = 16,
}

const Flags: number = 20;

function getEnumValues(flags: number): Enum[] {
    const enumFlags = [1, 4, 16];
    
    const enums: Enum[] = [];

    enumFlags.forEach(ef => {
        if ((ef & flags) != 0) {
            enums.push(ef);
        }
    })

    return enums;
}

console.log(getEnumValues(Flags));

Solution found here: Typescript flagged enum get values Is not good because my Enum is not a sequence 2 and 8 is missing.

CodePudding user response:

I have encountered a similar challenge in the past. And by coincidence, I have written an article about that, because it was a really interesting challenge.

You can find the link here: https://dev.to/sincovschi/bitwise-enum-flags-to-generate-html-from-array-3576

The first thing you need is a helper that extracts Enum's flags as an array by treating TS enum as an ordinary object. After that, your approach is good to go by looping over flags and see if it's present in your number.

Snippet copied from the link above's code sandbox.

const isPowerOfTwo = (x: number): boolean => {
  return x != 0 && (x & (x - 1)) == 0;
};

export function getEnumFlags<
  O extends object,
  K extends O[keyof O] = O[keyof O]
>(obj: O): K[] {
  const isFlag = (arg: string | number | K): arg is K => {
    const nArg = Number(arg);
    const isNumber = !Number.isNaN(nArg);
    return isNumber && isPowerOfTwo(nArg);
  };

  const enumFlags: K[] = [];

  Object.keys(obj).forEach(key => {
    const nKey = Number(key);
    if (isFlag(nKey)) {
      enumFlags.push(nKey);
    }
  });

  return enumFlags;
}

CodePudding user response:

If your concern is that you're repeating the Enum values in the enumFlags array in the function, you can get them dynamically:

const enumFlags = Object.values(Enum)
    .filter((value): value is number => typeof value === "number");

You need to make the filter callback a type guard function (using a type predicate, value is number) because otherwise TypeScript thinks the array is of type (string | Enum)[]. But we know it isn't, because we're doing typeof === "number".

Playground link

Since building that array isn't free it may be worth doing that once, just after the Enum definition, and reusing it. That said, while it's not free, it's really cheap (and keeping it isn't free either: it occupies [a tiny bit of] memory).

  • Related