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"
.
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).