Understanding the inner workings of TypeScript when defining an enum is valuable. It goes beyond just specifying a type; it also generates an object in the resulting JavaScript code. This object in your scenario looks like:
var CookieOptions = {
None: 0,
Necessary: 1 << 0,
Analytical: 1 << 1,
Marketing: 1 << 2,
Personalization: 1 << 3,
All: ~(~0 << 4),
[0]: 'None',
[1 << 0]: 'Necessary',
[1 << 1]: 'Analytical',
[1 << 2]: 'Marketing',
[1 << 3]: 'Personalization',
[~(~0 << 4)]: 'All',
};
(The bidirectional mapping means you can retrieve the name from the value and vice versa. If multiple names map to the same value, the returned name is essentially arbitrary.)
Therefore, CookieOptions.Necessary
and similar are fundamentally just numbers. TypeScript may treat them as something more special, but fundamentally they are not.
Furthermore, TypeScript cannot determine which flag combinations within an enum are valid. It assumes your knowledge when using expressions like
CookieOptions.Necessary | CookieOptions.Analytical
and allows the result to be of type
CookieOption
, despite potentially being otherwise. This relaxed approach is for practicality's sake, as
flag enums in TypeScript lack strict typing.
Thus, there's no necessity to "convert an integer to a flag enum" - it already exists as one. When required, you can explicitly cast it for TypeScript's clarity:
var copts = 3 as CookieOptions;
// var copts: CookieOptions = 3; would result in an error
Alternatively, if the aim is to extract flag names as an array of strings, utilizing the enum object's nature mentioned previously, a helper function like this can be helpful:
function getFlagNames<E extends {[value: number]: string}>(e: E, v: number) {
let names: string[] = [];
while (v !== 0) {
const topBit = 1 << (31 - Math.clz32(v));
names.unshift(e[topBit]);
v &= ~topBit;
}
return names;
}
// Example:
console.log(getFlagNames(CookieOptions, CookieOptions.Necessary | CookieOptions.Marketing));
// => ["Necessary", "Marketing"]
Remember to pass in CookieOptions
itself. This function will only yield meaningful results if there's an enum value for each flag bit set in v
.
Similarly, here's a utility function enabling the retrieval of a flags value based on an enum object and an array of strings:
function getFlagsValue<E extends {[k: string]: number | string}>(e: E, names: string[]) {
let flags = 0;
for (const name of names) {
flags |= e[name] as number;
}
return flags;
}
// Example:
console.log(getFlagsValue(CookieOptions, ['Necessary', 'Marketing']));
// => 5