Converting enum flags to strings

I'm encountering an issue with converting enum flags to strings and parsing them. Here is an example of my enum:

enum CookieOptions {
  None = 0,
  Necessary = 1 << 0,
  Analytical = 1 << 1,
  Marketing = 1 << 2,
  Personalization = 1 << 3,
  All = ~(~0 << 4),
}

I want to store this enum as a string, like in a cookie or local storage. However, converting it to a string simply results in it being cast as an integer. How can I parse it so that I can still use it in the following way:

if (options & CookieOptions.Analytical) {
  ...
}

In essence, how do I convert an integer back to a flag enum?

Answer №1

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having trouble showing JSON data with Ionic 2 and Xcode?

Extracting JSON data from a JSON file in my project and viewing it using "ionic serve" on my Mac has been successful. However, I am facing an issue after building for IOS in XCode. I import the generated project into XCode as usual, but the JSON data is no ...

Uploading multiple files simultaneously in React

I am facing an issue with my React app where I am trying to upload multiple images using the provided code. The problem arises when console.log(e) displays a Progress Event object with all its values, but my state remains at default values of null, 0, and ...

Building a Dynamic Checkbox Validation Feature in Angular Using Data retrieved from an API

Currently, I have a function that retrieves and displays a list obtained from an API: displayEventTicketDetails() { this.Service .getEventTicketDetails().subscribe((data: any) => { this.eventTicketDetails = data.map(ticket => ticket. ...

Converting React useState to a JSON object data type

I imported a JSON data file using the following code: import data from "../data.json"; The contents of the file are as follows: [ { "name": "Emery", }, { "name": "Astrid", }, { " ...

Arranging Pipe Methods in RxJS/Observable for Optimal Functionality

In the class Some, there is a method called run that returns an Observable and contains a pipe within itself. Another pipe is used when executing the run method. import { of } from 'rxjs'; import { map, tap, delay } from 'rxjs/operators&ap ...

Unable to retrieve input value in ReactJS with TypeScript

I've just started learning Typescript and I encountered an error while trying to console.log the input field value. Any tips or suggestions on how to handle this? Here's my code: class Register extends Component<{},userState> { state = { ...

The function in Angular 5/Typescript disappears when attempting to call it from within another function

After importing D3 into my component, I encounter an issue when trying to assign a layout to the D3.layout property. Strangely, although the layout property is present in the console output of my D3 object, it seems to be unknown when I attempt to call i ...

Why does the error message "DeprecationWarning: 'originalKeywordKind' deprecated" keep popping up while I'm trying to compile my project?

Upon completing the compilation of my project, I encountered the error message provided below. What does this signify and what steps can I take to resolve it? - info Linting and checking validity of types DeprecationWarning: 'originalKeywordKind' ...

The request/response is missing property "x" in type "y" but it is required in type "z" during fetch operation

I have configured an interface specifically for utilization with an asynchronous function: interface PostScriptTagResponse { script_tag : { readonly id : number , src : string , event : string , readonly created_at : string , readonl ...

Add a React component to the information window of Google Maps

I have successfully integrated multiple markers on a Google Map. Now, I am looking to add specific content for each marker. While coding everything in strings works fine, I encountered an issue when trying to load React elements inside those strings. For ...

TypeScript encounters a self-referencing type alias circularly

Encountering an issue with Typescript 3.6.3, specifically getting the error: Type alias 'JSONValue' circularly references itself. View code online here In need of assistance to resolve the circular reference in this specific version of TS (note ...

Creating adaptable Object Properties using Zod

Just dipping my toes into Typescript, Zod, and Trpc. Let's say I have a schema for animals and plants. I want to keep all their shared properties in the main part of the schema, while putting more specific details into a sub-object named custom. (jus ...

Issue with TypeORM findOne method causing unexpected output

I am encountering an issue with my User Entity's Email Column when using TypeORM's findOne function. Instead of returning null for a non-existent email, it is returning the first entry in the User Entity. This behavior does not align with the doc ...

What is the best way to centralize JSDoc typedef information for easy sharing between different projects?

Currently, I am incorporating @typedef JSDoc comments at the beginning of my Javascript files to define types (primarily to access certain benefits of TypeScript without fully diving into it right now). I'm curious, where can I keep JSDoc typedef inf ...

The source files are expected to be contained within the 'rootDir' directory, which is not located at 'c:/Users/hasit/Desktop/typescript/src'

Can someone assist me with separating the Src folder and public folder in my project? When I try to do it in the tsconfig.json file, I encounter this error: "'rootDir' is expected to contain all source files." I have followed instructions from a ...

Error with SwitchMap on ActivatedRoute.paramMap

When I try to run the ngOnInit method of my component, I encountered an error with the following line of code. this.products$ = this.route.paramMap.switchMap((params: ParamMap) => this.getProductsForType(params.get('type'))); The error mes ...

Unraveling the mystery of nested optional indexes in interfaces

Discover the interface outlined on this TS playground export type GetTestimonialsSectionQuery = { __typename?: 'Query', testimonialsSection?: { __typename?: 'TestimonialsSection', id: string, testimonials: Array< ...

Apply CSS styling to the shadow root

In my preact project, I am creating a Shadow DOM and injecting a style element into the Shadow root using the following code: import style from "./layout/main.css"; loader(window, defaultConfig, window.document.currentScript, (el, config) => ...

Is there a method to automatically eliminate all unnecessary properties in an Angular project?

In my extensive Angular project, I suspect that there are numerous declared properties in .component.ts that are not being used. Is there a method available to automatically eliminate all unused properties within an Angular project while also taking into ...

Spotlight a newly generated element produced by the*ngFor directive within Angular 2

In my application, I have a collection of words that are displayed or hidden using *ngFor based on their 'hidden' property. You can view the example on Plunker. The issue arises when the word list becomes extensive, making it challenging to ide ...