Extending Enums in Typescript: A Comprehensive Guide

How can you work with a list of constants or Enum? Here is an example:

enum MyList
{
    A,
    B
}

enum MyList2
{
    C
}


function process<T>(input:MyList | T):void
{

}

process<MyList2>(123) // The compiler does not recognize that 123 is not supported

Answer №1

This issue has been around for a while, especially with numeric enums. It seems that due to some backwards compatibility concerns, a number can be assigned to a numeric enum without triggering an error:

enum E {
  V = 100
}
const num = 100;
const e: E = num; // no error ðŸĪ”

In addition, numeric enums are designed to function as bit fields, which means they do not restrict the values to only those declared in the enum:

enum Color {
  RED = 1,
  GREEN = 2,
  BLUE = 4
}
const red: Color = Color.RED; // 1
const yellow: Color = Color.RED | Color.GREEN; // 3 ðŸĪ”
const white: Color = Color.RED | Color.GREEN | Color.BLUE; // 7 ðŸĪ”
const octarine: Color = Math.pow(Color.BLUE - Color.RED, Color.GREEN); // 9 ðŸĪŠ

It's quite strange that you can perform any mathematical operation with a numeric enum. Essentially, any number can be assigned to any numeric enum, and vice versa.


To avoid this behavior, you might want to consider creating your own types and values with controlled behavior instead of using enums. While more verbose, it can help meet your requirements:

const MyList = {
  A: 0,
  B: 1
} as const;
type MyList = typeof MyList[keyof typeof MyList]

const MyList2 = {
  C: 0
} as const;
type MyList2 = typeof MyList2[keyof typeof MyList2]

These custom types behave similarly to enums but are much stricter about their behavior:

function test<T>(input: MyList | T): void {}

test(0); // okay
test(1); // okay
test(2); // okay, 2 is inferred as T
test<MyList2>(123); // error! 123 is not assignable to 0 | 1

I hope this information is helpful. Best of luck!

Link to code

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

Compatibility issues arise with static properties in three.d.ts when using the most recent version of TypeScript

When compiling three.d.ts (which can be found at this link) using the TypeScript develop branch, an error occurs with the following message: Types of static property 'Utils' of class 'THREE.Shape' and class 'THREE.Path' are i ...

Issues with Typegoose and Mongoose Enums when utilizing an array of strings

One of my enums is defined as follows: export enum Careers { WEB_DEVELOPMENT = 'Web Development', MOBILE_DEVELOPMENT = 'Mobile Development', UI_UX = 'UI/UX' } This particular enum is used as a mongoose property like so: ...

Utilize string variables within TypeScript's enumeration feature

Can string variables be used in enums in TypeScript? Strings can be used in enum like so: enum AllDirections { TOP = 'top', BOTTOM = 'bottom', LEFT = 'left', RIGHT = 'right', } However, trying to use variab ...

Determine the full location information with the help of Google Maps SDK without the need

My current challenge involves dealing with a list of unformatted and incorrectly written addresses. I am seeking a way to iterate through these flawed strings and generate more organized and accurate addresses using one of the many Google Maps SDKs availa ...

Issues encountered when attempting to use @rollup/plugin-json in conjunction with typescript

I have been encountering an issue with my appsettings.json file. Despite it being validated by jsonlint.com, and having the tsconfig resolveJsonModule option set to true, I am facing difficulties while importing @rollup/plugin-json. I have experimented wit ...

Navigating a vast JSON dataset containing identical key names: A step-by-step guide

I am faced with a massive json file that has the following format: name: 'xxx', worth: [123, 456, 789] children: [ {name: 'xxx', worth: [987, 654, 321], children: [ {name: 'xxx', ...

Module not found in Typescript

Filter.ts, mustache.js and redux.3.5.2.js are all located in the same directory (/Scripts). The code snippet within Filter.ts is as follows: ///<reference path="./typings/mustache.d.ts" /> import Mustache = require("mustache"); import Redux = requir ...

What are the steps to resolve routing issues within an Angular component integrated into an ASP.NET Core web application?

When it comes to routing in Angular, I have been following the Angular tutorial from documentation. My example application integrates Angular components with an ASP.NET Core 2.2 web app, and these components are displayed within .cshtml views. I use Angul ...

Issue with Angular 4 Routing: Links are opening in new window instead of within router-outlet

I am currently facing an issue while trying to display the SuburbDataComponent HTML on the DASHBOARD-SIDE-PANEL-COMPONENT.HTML. When I click on Dashboard, it opens a new window but only displays the SuburbDataComponent.html without showing the side panel ...

Search for an element deep within a tree structure, and once found, retrieve the object along with the specific path leading to

I created a recursive function to search for a specific object and its path within a tree structure. However, when I changed the target ID (from 7 to 10) in the function, I encountered an error: "message": "Uncaught TypeError: Cannot read ...

How to import a page from a different component in the Next.js application router

I am currently utilizing the Next.js app router and have organized my folders as follows: app/ ├─ /companies/ │ ├─ page.tsx ├─ /administrators/ │ ├─ page.tsx My objective is to import the companies/page.tsx component into the admini ...

Guide to incorporating ThreeJS Collada loader with TypeScript / Angular CLI

I currently have three plugins installed: node_modules/three My Collada loader was also successfully installed in: node_modules/three-collada-loader It seems that the typings already include definitions for the Collada loader as well: node_modules/@ty ...

Encountering an issue during the initialization of the Google Passportjs

I recently made the switch from JavaScript to TypeScript in my server project and I'm currently tidying up some code. I decided to combine my Google Passport OAuth stuff and login routes into a single file, but it seems like I've broken something ...

Determining the generic type argument of a class can be unsuccessful due to the specific properties within that class

Why is it that Typescript sometimes fails to infer types in seemingly simple cases? I am trying to understand the behavior behind this. When Typescript's Type Inference Goes Wrong Consider the scenario where we have the following class declarations: ...

The function with which you are trying to use 'new' does not have a call or construct signature

How can I prevent the error from appearing in my console.log? An error message - 'Cannot use 'new' with an expression whose type lacks a call or construct signature.' - keeps popping up. var audioContext = new window.AudioContext() ...

Implementing service injection within filters in NestJS

Looking to integrate nestjs-config into the custom exception handler below: import { ExceptionFilter, Catch, ArgumentsHost, Injectable } from '@nestjs/common'; import { HttpException } from '@nestjs/common'; import { InjectConfig } fro ...

Deactivate the button until the input meets validation criteria using Angular

What I'm trying to achieve is to restrict certain input fields to only accept integer or decimal values. Here's the code snippet that I currently have: <input type="text" pattern="[0-9]*" [(ngModel)]="myValue" pInputText class="medium-field" ...

Deciding between bundling a Typescript package and using tsc: When is each approach the best choice

When it comes to publishing a Typescript NPM package (library, not client), I have two main options: 1. Leveraging Typescript's compiler First option is to use the Typescript compiler: tsc and configure a tsconfig.json file with an outDir setting: { ...

A tip for transferring the value of a binding variable to a method within the template when the button is clicked

I am currently exploring the concept of binding between parent and child components using @Input, @Output, and EventEmitter decorators. This is demonstrated in the HTML snippet below: <h1 appItemDetails [item]="currentItem">{{currentItem}}& ...

I seem to be missing some properties in the request body schema. Why am I receiving an incomplete model for

Seeking assistance in grasping the working of models in loopback4. Here's a model I defined: @model() export class ProductViewConfig extends BaseConfig { @property({ type: 'string', id: true, generated: true, }) _id?: strin ...