Exploring the contrast between string enums and string literal types in TypeScript

If I want to restrict the content of myKey in an object like { myKey: '' } to only include either foo, bar, or baz, there are two possible approaches.

   // Using a String Literal Type 
   type MyKeyType = 'foo' | 'bar' | 'baz';

    // Alternatively, with a String Enum   
    enum MyKeyType {
       FOO = 'foo',
       BAR = 'bar',
       BAZ = 'baz'
    }

I am curious about the advantages and disadvantages of each method, as they seem quite similar to me (except for how I would access the values for conditions).

The only distinction I could find in the TypeScript documentation is that Enums are actual objects at runtime, which may be useful in certain scenarios.

Answer №1

There exists a distinguishing factor between string enums and literal types in the transpiled code.

Let's compare the Typescript Code

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

Now let's observe the transpiled JavaScript Code

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));

It can be seen that no generated code is produced for the string literal. This is because Typescripts Transpiler is solely utilized for ensuring type safety during transpilation. At runtime, string literals are converted to regular strings with no connection between the definition of the literal and its usage.

Hence, there is an additional option known as const enum

Consider this example

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

// or with a Const String Enum   
const enum MyKeyType3 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

var a : MyKeyType1 = "bar" 
var b: MyKeyType2 = MyKeyType2.BAR
var c: MyKeyType3 = MyKeyType3.BAR

This will be transpiled as follows

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
var a = "bar";
var b = MyKeyType2.BAR;
var c = "bar" /* BAR */;

If you want to experiment further, you can access this link

I personally opt for the const enum scenario due to the ease of typing Enum.Values. Typescript efficiently handles the conversion process to achieve optimal performance during transpilation.

Answer №2

An important concept to grasp is that string enums have opaque values.

The purpose of a string enum is to keep the actual string value behind MyKeyType.FOO hidden from other code. This means you cannot directly pass the actual string "bar" to a function expecting a MyKeyType - you must use MyKeyType.BAR instead.

Answer №3

One advantage of using an enum during development is the ability to easily view a list of options through intellisense:

https://i.stack.imgur.com/X3IJr.png

In addition, you can quickly change an enum value with refactoring tools, rather than having to update every occurrence of a string.

Edit: From VS 2017 and TypeScript version 3.2.4 onwards, intellisense now supports string literal types:

https://i.stack.imgur.com/ZeRsx.png

Answer №4

One major drawback of enums is that when using numbers instead of strings, the entire enum may not be secure in my view. This is because any number value can be assigned to a variable of this type.

enum GENDER {MALE = 1, FEMALE = 2, BOY = 3, GIRL = 4};
let bar: GENDER = GENDER.MALE;
bar = 37.14; //compiler won't raise issue

Answer №5

Utilizing enum over string literals offers the advantage of being able to use it in areas where type declaration is not specified.

For instance:

assert.equal(data.type, DataType.BAR)

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

Is there a way to update JSON data through a post request without it getting added to the existing data?

Hello, I am currently delving into Angular2 and encountering a problem concerning RestAPI. When I send a post request to the server with a JSON file, I intend to update the existing data in the JSON file; however, what actually happens is that the new data ...

extract keys and values from an array of objects

I would like assistance with removing any objects where the inspectionScheduleQuestionId is null using JS. How can we achieve this? Thank you. #data const data = [ { "id": 0, "inspectionScheduleQuestionId": 1, ...

ESLint is notifying that the prop validation for ".map" is missing, with the error message "eslint react/prop-types" occurring in a Typescript React environment

Hey everyone, excited to be posting for the first time! Currently, I'm working on a small project using Typescript and React. I've run into an issue with ESLint where it doesn't recognize that a prop variable of type string[] should have a ...

Visual Studio 2015 does not support compiling typescript files

I'm encountering some difficulties while attempting to set up node with typescript support in Visual Studio 2015 for my web API application. To start fresh, I deleted the node_module folder along with the package.json and tsconfig.json files. Followi ...

Something went wrong trying to retrieve the compiled source code of https://deno.land/[email protected]/path/mod.ts. It seems the system is unable to locate the specified path. (os error 3)

Upon executing my main.ts script using deno, I encounter the following error message: error: Failed to retrieve compiled source code from https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bfcccbdbff8f918a86918f"& ...

TypeScript failing to recognize dependency for LitElement

Currently, I am encountering an issue with importing two lit elements in my project, namely RootElement and CustomElement. The problem arises when trying to import CustomElement, which unlike RootElement does not show up properly on the UI. My attempt to i ...

Creating a custom extended version of Angular2 Http does not automatically provide injection of services

I'm struggling to understand this issue. I've created a custom class that extends Angular's Http class. import { Injectable } from '@angular/core'; { Http, ConnectionBackend, RequestOptions, RequestOptionsArgs, ...

Webpack 4.1.1 -> The configuration.module contains a property 'loaders' that is unrecognized

After updating my webpack to version 4.1.1, I encountered an error when trying to run it: The configuration object is invalid. Webpack has been initialized with a configuration that does not match the API schema. - The 'loaders' property in ...

Can TypeScript modules be designed to function in this way?

Seeking to create a versatile function / module / class that can be called in various ways: const myvar = MyModule('a parameter').methodA().methodB().methodC(); //and also this option should work const myvar = MyModule('a parameter') ...

Leveraging TypeScript to call controller functions from a directive in AngularJS using the "controller as"

I've encountered an issue while trying to call a controller function from a directive, specifically dealing with the "this" context problem. The logService becomes inaccessible when the function is triggered by the directive. Below is the code for th ...

How can I search multiple columns in Supabase using JavaScript for full text search functionality?

I've experimented with various symbols in an attempt to separate columns, such as ||, |, &&, and & with different spacing variations. For example .textSearch("username, title, description", "..."); .textSearch("username|title|description", "..."); U ...

How should a React Testing Library wrapper be properly typed in a TypeScript environment?

There's this code snippet from Kent C. Dodd's library that I find extremely helpful import * as React from 'react' import {render as rtlRender} from '@testing-library/react' import {ThemeProvider} from 'components/theme& ...

The NextJS API route functions flawlessly when tested locally, generating over 200 records. However, upon deployment to Vercel, the functionality seems to

Here is the API route that I am using: https://i.stack.imgur.com/OXaEx.png Below is the code snippet: import type { NextApiRequest, NextApiResponse } from "next"; import axios from "axios"; import prisma from "../../../lib/prisma ...

forEach was unable to determine the appropriate data types

define function a(param: number): number; define function b(param: string): string; define function c(param: boolean): boolean; type GeneralHooks<H extends (...args: any[]) => any> = [H, Parameters<H>] const obj = { a: [a, [1]] as Gene ...

Strange occurrences with HTML image tags

I am facing an issue with my React project where I am using icons inside img tags. The icons appear too big, so I tried adjusting their width, but this is affecting the width of other elements as well. Here are some screenshots to illustrate: The icon wit ...

Ways to determine if a date matches today's date within a component template

I am currently displaying a list of news articles on the webpage and I want to show the word "Today" if the news article's date is equal to today's date. Otherwise, I want to display the full date on the page. Is there a way to compare the news.D ...

Encountered an error while trying to access an undefined property in Angular

Trying to perform a basic import, but encountering a significant stack trace issue. Extensive search efforts have been made to address this problem, yet the stack trace lacks sufficient information for resolution. UPDATE: When setting a variable not sour ...

Trigger parent Component property change from Child Component in Angular2 (akin to $emit in AngularJS)

As I delve into teaching myself Angular2, I've encountered a practical issue that would have been easy to solve with AngularJS. Currently, I'm scouring examples to find a solution with Angular2. Within my top-level component named App, there&apos ...

Exploring the use of a customizable decorator in Typescript for improved typing

Currently, I am in the process of creating TypeScript typings for a JavaScript library. One specific requirement is to define an optional callable decorator: @model class User {} @model() class User {} @model('User') class User {} I attempted ...

What is the recommended data type to assign to the `CardElement` when using the `@stripe/react-stripe-js` package in TypeScript?

I'm struggling to determine the correct type to use for this import: import { CardElement } from '@stripe/react-stripe-js'; I have successfully used the types Stripe, StripeElements, and CreateTokenCardData for the stripe and elements props ...