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

Passing data from ModalService to a component

Currently, I am attempting to utilize the ngx-bootstrap-modal in order to transfer data from a modal service to a modal component. While reviewing the examples, it is suggested to use the following code: this.modalService.show(ModalContentComponent, {init ...

Flashing Screens with Ionic 2

I am currently dealing with a situation where my login page and homepage are involved. I have implemented native storage to set an item that helps in checking if the user is already logged in (either through Facebook or Google authentication). The logic fo ...

Using Typescript and JSX to render a component that has been passed as an argument

I am seeking to create a function that will render a React component passed as an argument. I aim to accommodate both Component and StatelessComponent types with the following approach: function renderComponent(component: React.ComponentClass<any> | ...

The specified property is not recognized by the type in TypeScript

I have set up a basic form with validation in Ionic 2. The form functioned properly when I used 'ionic serve' but encountered issues when running 'ionic run'. My suspicion is that the problem lies within my TypeScript code, specifically ...

What is the best way to retrieve class properties within an input change listener in Angular?

I am new to Angular and have a question regarding scopes. While I couldn't find an exact match for my question in previous queries, I will try to clarify it with the code snippet below: @Component({ selector: 'item-selector&apos ...

Updating props in a recursive Vue 3 component proves to be a challenging task

I am facing an issue with two recursive components. The first component acts as a wrapper for the elements, while the second component represents the individual element. Wrapper Component <template> <div class="filter-tree"> &l ...

Automatic verification of OTP in Ionic 3

Seeking assistance for implementing auto OTP verification in a project I am working on. After the user enters their phone number, I have come across some examples for Ionic 1 with Angular 1 online. However, I am specifically looking for examples using Io ...

What is the proper way to import and define typings for node libraries in TypeScript?

I am currently developing a node package in TypeScript that utilizes standard node libraries such as fs, path, stream, and http. Whenever I attempt to import these libraries in a .ts file, VS Code flags the line with an error message: [ts] Cannot find m ...

Encountering an undefined error while attempting to retrieve an object from an array by index in Angular

Once the page is loaded, it retrieves data on countries from my rest api. When a user selects a country, it then loads the corresponding cities for that country. Everything is functioning properly up to this point, however, upon opening the page, the city ...

What is the difference between TypeScript's import/as and import/require syntax?

In my coding project involving TypeScript and Express/Node.js, I've come across different import syntax options. The TypeScript Handbook suggests using import express = require('express');, while the typescript.d.ts file shows import * as ex ...

Oops! A mistake was made by passing an incorrect argument to a color function. Make sure to provide a string representation of a color as the argument next time

Encountering an issue with a button react component utilizing the opacify function from the Polished Library The styling is done using styled-components along with a theme passed through ThemeProvider. Upon testing the code, an error is thrown. Also, the ...

Facing issue with Angular 17 where pipe is displaying empty data

I am currently utilizing Angular 17 with the code provided below: database.component.html @for(user of (users | userPipe:filters); track user.id) { <tr id="{{ user.id }}"> <td>{{ user.name }}</td> <td> ...

Is it possible to safely remove a class instance containing a GLcontext within a react.FC State to prevent memory leaks, especially when using a "class object with THREE.js"?

I have successfully developed a react.FC() application. In this application, you have the ability to throw a bottle in the metaverse (like a message in a bottle) to be discovered in the future. The app retrieves information from an API and constructs a c ...

Determine if a variable contains only one digit in Angular 6 using Typescript

I have a specific value that I want to discuss: this.value.day It gives me a numerical output ranging from 1 to 31. My requirement is to add a leading zero if the number is less than 10. Can anyone guide me on achieving this? ...

Looking for guidance on where to find a functional code sample or comprehensive tutorial on working with ViewMetadata in Angular2

I am currently trying to understand the relationship between viewmetadata and the fundamental use of encapsulation: ViewEncapsulation, including ViewEncapsulation.Emulated and ViewEncapsulation.None. Here is a link for further information: https://angula ...

Angular2 webpack example error: Cannot execute function 'call' because it is undefined

As I navigate through the Angular2 webpack sample app on https://angular.io/docs/ts/latest/guide/webpack.html, I've encountered an issue. After completing the "Development Configuration" section and attempting the "try it out" by copying the app code ...

What causes inability for JavaScript to access a property?

My current coding project involves the usage of typescript decorators in the following way: function logParameter(target: any, key : string, index : number) { var metadataKey = `__log_${key}_parameters`; console.log(target); console.log(metadataKey ...

Is it necessary to declare parameters for the super class constructor in Typescript React? If so, what would those parameters be?

I'm struggling to understand the specifics of the constructor in this code. Can anyone clarify what arguments the constructor for super() and constructor() should have? import * as React from "react"; import styles from "./ScriptEditor.module.scss"; ...

Challenges faced with implementing Tailwind CSS within the pages directory of NextJS websites

Issue with Tailwind Styles I've encountered a problem where the Tailwind styles are not being applied to components in the /pages directory of my NextJS project. Oddly enough, the same component works fine when used outside the pages directory. When ...

Acknowledgment Pop-up

When using the PrimeNG table with a custom modal component that I created, I encountered an issue. The edit functionality works correctly and retrieves the correct row id, however, the delete function always returns the id of the first row. dashboard.html ...