Is there a deeper philosophical rationale behind choosing to use (or not use) enums in TypeScript, along with string union types?

Recently, I delved into the world of enum and const enum in Typescript, causing some confusion. I grasped that const enum gets transpiled into simple values while regular enums do not. I also recognized certain distinctions between using string union type versus a plain enum, such as iterating over values.

However, the question remains: when and why should one opt to use (or not use) (const) enum? The unpredictability of whether enumeration will be needed makes it challenging.

In a Korean post, it was mentioned that "tree-shaking cannot optimize transpiled enum since they are IIFE". (Link)

So, is it better to always go for const enum without hesitation, or are there additional pros and cons when considering alternatives like string union types?

TLDR

  1. If const enum is lighter than enum, why choose the latter in practical scenarios?
  2. Knowing that Babel does not support const enum, why do some still prefer enum over string union type, even if no iteration is required?

Answer №1

It is my belief that TypeScript inherited enums from its roots in C#. Numerical enums in TypeScript can be risky due to their lack of type safety. Consider the following example:

const enum Foo{
  a,
  b
}

const handle=(arg:Foo)=>arg

handle(42) // no error

This behavior is attributed to TypeScript's use of a structural type system rather than a nominal one.

Using non-const enum can result in excessive boilerplate code:

var Foo;
(function (Foo) {
    Foo[Foo["a"] = 0] = "a";
    Foo[Foo["b"] = 1] = "b";
})(Foo || (Foo = {}));

Therefore, I recommend avoiding the use of enums altogether. Instead, consider utilizing immutable objects. For example:

const Foo = {
  a: 1,
  b: 2
} as const;

type Values<T> = T[keyof T]

type Foo = Values<typeof Foo>;

const handle = (arg: Foo) => arg

handle(Foo.a) // ok
handle(Foo.b) // ok

handle(42) // error

On the topic of string unions, imagine this scenario:

enum Events {
  click = 'click',
  hover = 'hover'
}

type Union = 'click' | 'hover'

If you have been using Events.click in multiple files and then need to rename it to _click, you only need to make a simple adjustment in the enum declaration:

enum Events {
  click = '_click',
  hover = '_hover'
}

By using string unions instead of literals, you only have to update one type declaration. This approach offers efficiency and ease when making changes compared to manually updating each instance of a literal string.

Ultimately, the decision between enums and string unions depends on the specific use case at hand.

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

An effective way to retrieve a property from an observable by utilizing a pipe

I'm having trouble storing an OrderState object in my ngrx store and extracting data from it for my UI using the async pipe. Specifically, I want to retrieve a child property from this observable object. order.state.ts export interface OrderState { ...

Switch up the styling of a component by updating its properties with a switch statement

Although there is a similar question, my query has a unique requirement. I have defined the common styles for my button and implemented a function using a switch statement with different properties for various buttons across different pages. However, for ...

Angular (TypeScript) time format in the AM and PM style

Need help formatting time in 12-hour AM PM format for a subscription form. The Date and Time are crucial for scheduling purposes. How can I achieve the desired 12-hour AM PM time display? private weekday = ['Sunday', 'Monday', &apos ...

Converting a TypeScript object into a JSON string

When working with TypeScript, I am facing a challenge while trying to initialize an object that requires a JSON string for the "options" parameter. Specifically, it pertains to the object mentioned here. It is crucial that the options parameter be in JSON ...

Posting forms in NextJS can be optimized by utilizing onChange and keypress events for input fields

I am currently working on my first Edit/Update form within a newly created NextJs application as I am in the process of learning the framework. I seem to be facing an issue where the form constantly posts back to the server and causes the page to refresh ...

While working with Ngrx/effects, an error with code TS2345 occurred. The error message stated that the argument is of type 'Product[]', which cannot be assigned to a parameter of type

When I compile my code, I encounter the following issue (despite not finding any errors in the browser console and the application functioning properly). An error occurs in src/app/services/product.service.ts(15,9): The type 'Observable<Product> ...

Is it feasible to incorporate a method into a prototype and ensure that 'this' is associated with the appropriate type in TypeScript?

I have a scenario where I need to add a new method to a prototype, specifically to a class created using TypeScript. Here is an example: declare module "./MyClass" { interface MyClass { myNewMethod(); } } MyClass.prototype.myNewM ...

Error: In Angular Firebase, the type 'string' cannot be assigned to the type 'Date'

I am encountering an error. The following error is shown: "cannot read property 'toDate' of undefined. Without the toDate() | Date." After further investigation, I found: A Timestamp object with seconds=1545109200 and nanoseconds=0. A hel ...

Error Alert: Next.js TypeScript is reporting that the necessary packages are missing from your setup

As I work on developing a basic Next.js website using their TypeScript starter, everything was going smoothly with the 'yarn dev' command. However, out of nowhere, I started encountering an error message whenever I tried to run 'yarn dev&apo ...

The presence of a setupProxy file in a Create React App (CRA) project causes issues with the starting of react-scripts,

I have implemented the setupProxy file as outlined in the documentation: const proxy = require('http-proxy-middleware'); module.exports = function (app) { app.use( '/address', proxy({ target: 'http ...

TypeScript compilation will still be successful even in the absence of a referenced file specified using require

Having both Project 1 and Project 2 in my workspace, I encountered an unusual issue after copying a file, specifically validators/index.ts, from Project 1 to Project 2. Surprisingly, TypeScript compilation went through successfully without showing any erro ...

The typescript MenuProvider for react-native-popup-menu offers a range of IntrinsicAttributes

Looking to implement drop-down options within a Flatlist component, I've utilized the React Native Popup Menu and declared MenuProvider as the entry point in App.tsx. Encountering the following error: Error: Type '{ children: Element[]; }' ...

tips for managing response time in firebase authentication state

I've been facing an issue with my web application in efficiently checking if it is firebase authenticated. The 'auth state object' doesn't seem to be functioning correctly on my template, as the expected sections are not appearing at al ...

`What exactly do auth.guard.ts and the AuthenticationService do in Angular 8?`

import { Injectable } from '@angular/core'; import { AuthenticationService } from './_services'; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; @Injectable({ providedIn: & ...

eliminate any redundant use of generics

Recently, I attempted to create a pull request on GitHub by adding generics to a method call. This method passes the generically typed data to an interface that determines the return type of its methods. However, the linter started flagging an issue: ERR ...

How to identify generic return type in TypeScript

My goal is to develop a core dialog class that can automatically resolve dialog types and return values based on the input provided. I have made progress in implementing this functionality, but I am facing challenges with handling the return values. Each ...

How can I generate pure JavaScript, without using Typescript modules?

Take this scenario as an example ... index.ts import { x } from "./other-funcs"; function y() { alert("test"); } x(y); other-funcs.ts import { z } from "some-module"; export function x(callback: () => void): void { z(); callback(); } ...

Angular: ngx-responsive has a tendency to hide elements even if they meet the specified conditions

Recently, I started using a library called this to implement various designs for desktop and mobile versions of an Angular app (v4.2.4). Although the documentation recommends ngx-responsive, I opted for ng2-responsive but encountered issues. Even after set ...

Encountering an error during the registration process of @fastify/middie

I am currently in the process of integrating Fastify into a small project I am working on. One of the key requirements for this project is the utilization of Middleware (via @fastify/middie). However, when I follow the necessary steps to register the middi ...

The refresh function in the table is not working as expected when implemented in a functional component. The table being used is Material

I am currently utilizing MaterialTable from https://material-table.com/#/docs/features/editable to manage data and perform CRUD operations within my application. I am seeking a way to automatically refresh the table data after any CRUD operation (add, upda ...