TypeScript's version of Java's enum (or C#'s structure)

I'm facing the challenge of creating an enum in Typescript that mimics the functionality of Java enums. In TypeScript, only integer-based enums like C# are supported, unlike in Java where we can have custom objects with non-integer related properties within an enum. Is there a way to achieve this in Typescript without having to resort to workarounds?

My goal is to replicate an enum structure similar to this Java code snippet in Typescript:

public enum Something { 

 PENNY("PENNY"), NICKLE("NICKLE"); 

 private String value; 

 private Something (String value) { 
  this.value = value; 
 } 

};

Answer №1

Currently, the current programming languages like Java or C# do not have native support for enums. However, I have come up with a workaround solution.

/**
 * Decorator for Enum.
 * @param {string} idPropertyName - property name to find enum value by property value. Usage in valueOf method
 * @return constructor of enum type
 */
export function Enum<T = any>(idPropertyName?: keyof T) {
    // Implementation details go here...
}

// Other types and interfaces related to Enums...

/** 'Casting' method to make correct Enum Type */
export function EnumType<T>(): IStaticEnum<T> {
    return (<IStaticEnum<T>> Enumerable);
}

Now, using enums has become much simpler

// Example usage of the Enum workaround

@Enum("value")
class Something extends EnumType<Something>() {

    static readonly PENNY = new Something("Penny");
    static readonly NICKLE = new Something("Nickle");

    constructor(readonly value: string) {
        super();
    }
}

// Sample usage of the created Enum
console.log("" + Something.PENNY);              
console.log("" + Something.NICKLE);             
console.log(Something.values());                
console.log(Something.valueByName("PENNY"));    
console.log(Something.PENNY.enumName);          

All the solutions provided above ensure type safety when working with Enums.

Answer №2

Even though TypeScript enums can only hold numeric values, it is still possible to access their names as well as their indexes. To get the name of an enum value, you can pass the number back to the enum itself:

enum Color { Red, Green, Blue }

console.log(Color[Color.Red]); // "Red"

Another approach is to utilize a Map for this purpose:

const colorText = new Map([
  [Color.Red, 'RED'],
  // Add more mappings here...
]);

Answer №3

After some research on this topic, I discovered that TypeScript has included support for String Enums since version 2.4. This feature is similar to Java's enum and very user-friendly.

Check out the release notes for TypeScript 2.4 here:

String Enums

In TypeScript 2.4, enum members can now have string initializers.

enum Colors {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE", }

One thing to note is that enums initialized with strings cannot be reverse-mapped to retrieve the original enum member name. In other words, you cannot use Colors["RED"] to get the string "Red".

Answer №4

Creating an enum is as simple as this and it functions similarly to Java or C#.

export enum ControlType {
  INPUT,
  SELECT,
  DATEPICKER
}


console.log(ControlType.INPUT); // returns 0
console.log(ControlType[ControlType.INPUT]); // returns INPUT

You can include additional information with numeric values only.

export enum ControlType {
  INPUT = 3,
  SELECT = 6,
  DATEPICKER = "abc".length // computed member
}

console.log(ControlType.INPUT); // returns 3

If you need to add members that are not numbers, you can use <any> type.

export enum ControlType {
  INPUT = <any>'input',
  SELECT = <any>'select',
  DATEPICKER = <any>'date-picker'
}

console.log(ControlType.INPUT); // returns 'input'

The limitation is that you can only compare these values with variables of type any. For example;

(ControlType.INPUT === myVar) {..}

In this case, myVar must be declared as myVar: any = 'input'; even if it's a string.

Answer №5

Many individuals are unaware of the true essence of Java enums, which consist of a collection of static singletons sharing a common base class.

This concept greatly differs from primitive-based enums. In C#, one has the ability to incorporate extension methods into an enum, but lacks polymorphism functionality. Meanwhile, in Typescript, usage of a static function is required.

Moreover, there exists a relatively new paradigm of sealed classes that do not rely on static elements, enabling the utilization of interfaces instead of abstract base classes. These classes can directly participate in constructor-based dependency injection, unlike enums which only allow for property injection.

C# may also offer sealed classes, whereas Typescript does not possess final or even sealed options, necessitating the emulation through a parallel interface and factory coupled with a switch statement.

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

The getStaticProps() function in NextJS has not been invoked

Currently, I am working on a basic website and my goal is to retrieve data from an API and showcase it on my component. However, I've encountered an issue where the getStaticProps() method doesn't seem to be triggering. Below is the code snippet ...

Struggling with the compilation of this Typescript code

Encountering a compile error: error TS2339: Property 'waitForElementVisible' does not exist on type 'signinPage' SigninPage code snippet: export class signinPage{ constructor(){ emailInput: { selector: 'input[type ...

Creating organized lists in Angular 4 using list separators

I'm struggling to organize a list with dividers between categories to group items accordingly. Each divider should be labeled with the month name, and the items under it should correspond to that specific month. My Goal: - August - item 1 - item ...

What is the best way to set up the parser and plugins using ESLint's updated flat configuration?

How can ESLint be configured using the new "flat config" system (specifically with the eslint.config.js file) to work seamlessly with both @typescript-eslint/eslint-plugin and /parser? I have been struggling to make ESLint's new configuration system ...

Component html element in Angular not being updated by service

Within my Angular service, I have a property linked to a text field in a component's HTML. Oddly, when this property is updated by the service, the new value doesn't reflect in the HTML element unless the element is clicked on. I'm perplex ...

What is the best way to output a JSX element using an inline switch statement?

I have been attempting to use an inline switch in order to return an element, but all I am getting is an empty <span> </span>. What could be the issue here? getRowTdForHeader: (header: string, entry: response) => { return (< ...

What could be the reason for the exclusion of 'null' from the return type of Document.getElementById in VS Code?

Versions of VS Code: https://i.sstatic.net/nd5cD.png Experimenting with 'Type Narrowing' Code in VS Code has brought to my attention a discrepancy between the information provided by VS Code and TypeScript Playground: In VS Code, it shows that ...

Issue: Unrecognized element type in next.js while starting development server

Every time I run npm run dev, I encounter the following error: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from th ...

Creating robust unit tests for Node.js applications with the help of redis-mock

I am facing an issue while trying to establish a connection with redis and save the data in redis using the redis-mock library in node-typescript, resulting in my test failing. Below is the code snippet for the redis connection: let client: RedisClientTyp ...

React TSX file not recognizing JSON data stored in an HTML data attribute

I am having some trouble with implementing the password toggle component from the Preline UI. Here is how the component looks: "use client" import React, { ChangeEvent, MouseEventHandler, useEffect } from "react"; export default functi ...

In production mode, ExpressJs dispatches the stack efficiently

Before going live, I want to test production simulation with the following setup: package.json "start": "cross-env NODE_ENV=production node dist/index.js", index.ts console.log(process.env.NODE_ENV) // prints "production" ro ...

How can I retrieve the `checked` state of an input in Vue 3 using Typescript?

In my current project, I am using the latest version of Vue (Vue 3) and TypeScript 4.4. I am facing an issue where I need to retrieve the value of a checkbox without resorting to (event.target as any).checked. Are there any alternative methods in Vue tha ...

What is the best way to send multiple parameters to @Directives or @Components in Angular using TypeScript?

I am facing some confusion after creating @Directive as SelectableDirective. Specifically, I am unclear on how to pass multiple values to the custom directive. Despite my extensive search efforts, I have been unable to find a suitable solution using Angula ...

Tips for delivering a variable to a React Native Stylesheet

Is there a way to pass a variable to the "shadowColor" property in my stylesheet from an array declared in the code above? I keep encountering a "Can't find name" error. Attempting to use a template literal has not resolved the issue. Any assistance w ...

A guide on accessing a dynamic object key in array.map()

How can I dynamically return an object key in array.map()? Currently, I am retrieving the maximum value from an array using a specific object key with the following code: Math.max.apply(Math, class.map(function (o) { return o.Students; })); In this code ...

JavaScript heap running out of memory after upgrading from Angular 11 to versions 12, 13, or 14

I need assistance with resolving a JS heap out of memory issue that has been occurring when trying to start the local server ever since migrating from Angular 11 to Angular 12 (or 13 or 14, all versions tested with the same problem). This occurs during th ...

Error message stating: rxjs and firebase encountered a TypeError when attempting to add property 0 because the object is not

My angular application interacts with firebase firestore as the backend database. I am working on a function to retrieve document snapshots from firestore in a generic way. Here is the code snippet where I encounter an error: /** * Get a 'liste ...

The use of aliases is not supported by a published node module

I have a project built using create-react-app and it's utilizing react-app-rewired/react-scripts-ts. Within my tsconfig file, I've configured it as follows: baseUrl: "src", paths: { "src/*": "./*" } In many files within this project, the pa ...

`Is there a way to manage date formats across all components using a single method in Angular?`

I need assistance with controlling the date format of dates displayed in different components using one typescript file. How can I achieve this? app.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', ...

What is the best way to implement a <Toast> using blueprintjs?

Struggling without typescript, I find it quite challenging to utilize the Toast feature. This component appears to have a unique appearance compared to the others. Shown below is an example code. How would you convert this to ES6 equivalent? import { But ...