What is the best way to search for an Enum based on its value?

One of my challenges involves an enum containing various API messages that I have already translated for display in the front-end:

export enum API_MESSAGES {
  FAILED_TO_LOAD = 'Failed to load data',
  TOKEN_INVALID = 'Token seems to be invalid',
}

The goal is to utilize the error message received from the back-end to fetch the corresponding translation:

onError: (error: AxiosError<ApiError>) => {
  const errorRes = error.response?.data.error;
  console.log(API_MESSAGES[errorRes])
}

However, attempting this results in the following TypeScript error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof API_MESSAGES'

No index signature with a parameter of type 'string' was found on type 'typeof API_MESSAGES'

I have explored several options but haven't discovered a straightforward way to access the enum values.

Answer №1

limitations of reverse mappings in string enums

API_MESSAGES[errorRes] triggers an error due to the absence of reverse mappings for string enums, as explained in the Typescript Handbook:

Understanding Reverse Mappings

Numeric enum members possess both forward (name -> value) and reverse (value -> name) mappings, whereas string enum members lack reverse mapping functionality.

A numeric enum compiles into an object containing both forward and reverse mappings.

It's essential to note that string enum members do not receive automatic generation of a reverse mapping.

the importance of ignoring reverse mappings

The primary objective is to showcase the "translated" API message in a human-friendly manner. Even if support existed for string enums within API_MESSAGES[errorRes], the outcome would be:

'Token seems to be invalid' --> TOKEN_INVALID 

This represents the opposite of the desired result.

Furthermore, backend alignment with enum definition simplifies matters!

If the back-end code aligns with the same API_MESSAGES enum, it simply uses "a set of named constants", such as FAILED_TO_LOAD and TOKEN_INVALID, where these values serve as the translated messages themselves.

Hence, serialized API_MESSAGES transmission from the back-end already contains the "translated" message.

Consequently, your onError function becomes:

onError: (error: AxiosError<ApiError>) => {
  const errorRes = error.response?.data.error;
  console.log(errorRes)
}

Therefore, based on Typescript principles and logical deductions, error.response?.data.error holds the type API_MESSAGES. This implies that error.response?.data.error effectively represents the following union type

type API_MESSAGES = 'Failed to load data' | 'Token seems to be invalid',

To reiterate, the translated value already exists. FAILED_TO_LOAD and TOKEN_INVALID merely serve as the Typescript names for those values. In case the back-end mirrors the enum definition.

Alternatively, underlying codes necessitate mapping objects

If FAILED_TO_LOAD and TOKEN_INVALID denote the actual message strings sourced from the backend, employing a mapping object instead of an enum proves beneficial:

export const API_MESSAGES = {
  FAILED_TO_LOAD = 'Failed to load data',
  TOKEN_INVALID = 'Token seems to be invalid',
} as const;

// type API_MESSAGE = 'FAILED_TO_LOAD' | 'TOKEN_INVALID'
export type API_MESSAGE = keyof typeof API_MESSAGES

The error code transmitted from the backend materializes as a plain string, diverging from a typed Enum. The convertServerMessage function showcases the validation and conversion process to the API_MESSAGE enum. As previously mentioned, this activity requires execution at some juncture. Integrating this logic directly into the section of your code responsible for processing the server response and constructing the AxiosError object, wherein AxiosError.response.data.error assumes the type API_MESSAGE, obviates the need for a separate method.

export function convertServerMessage(msg: string): API_MESSAGE {
    if (msg in API_MESSAGES) {
        return msg as API_MESSAGE
    } else {
        // customized error handling can be implemented here
    }
}

Subsequently, the initial onError function operates as intended (refer to endnote):

onError: (error: AxiosError<ApiError>) => {
    // For simplicity, consider relocating the string-to-enum conversion
    // elsewhere within your codebase. It's currently placed here as an example.
    const errorRes = convertServerMessage(error.response?.data.error);
    console.log(API_MESSAGES[errorRes])
}

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

Using Inheritance to Create Custom Event/Callback Handlers in TypeScript

Currently facing an issue with Typescript that I'm stuck on. I have created a named callback Handler class that receives the allowed list of "events"/"handlernames" as a generic: class NamedHandler<H extends { [key: string]: HandlerFunction }> ...

When you hover over the button, it seamlessly transitions to a

Previously, my button component was styled like this and it functioned properly: <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', height: 48, padding: '0 ...

Is it possible for React props to include bespoke classes?

In our code, we have a TypeScript class that handles a variety of parameters including type, default/min/max values, and descriptions. This class is utilized in multiple parts of our application. As we switch to using React for our GUI development, one of ...

Is there a way to ensure that a certain block of code in Typescript is executed only after an API call has been completed?

When making an API call, I need the code after the call to wait until the API call finishes. In my function called this.api_call, it calls the API and only returns an array returnValue once the call is complete. let returnValue = this.api_call(data); // ...

Issue with calling a function to change the CSS color class of a button in Angular

Within my Angular code, I am attempting to set a CSS color for my button by calling a TypeScript function that will return the appropriate CSS class name. This is the code I have tried: <button style="height: 10%" class="getColor(days.date)">{{days ...

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 ...

Ways to simulate objects in jest

I'm facing an issue while trying to mock a function of an object using Jest and Typescript. Below is a concise version of my code: // myModule.ts export const Foo = { doSomething: () => { // ... does something console.log('original ...

Execute an Asynchronous Operation in NgRx After Triggering an Action

Please note that this is a question seeking clarification Instructions Needed I am currently working on dispatching an action to NgRx in order to add a task to a list of tasks. Additionally, I need to perform a put request to an API to save the changes ma ...

"Encountered an error in Angular: ContentChild not found

Working with Angular 5, I am attempting to develop a dynamic component. One of the components is a simple directive named MyColumnDef (with the selector [myColumnDef]). It is used in the following: parent.compontent.html: <div> <my-table> ...

Error message: Cypress Vue component test fails due to the inability to import the Ref type (export named 'Ref' is missing)

Recently, I created a Cypress component test for my Vue component by mounting it and verifying its existence. The component utilizes Vue's Ref type and is structured as a TypeScript component. However, during the test execution, Cypress encountered a ...

Consolidating Typescript modules into a single .js file

Recently, I was able to get my hands on a TypeScript library that I found on GitHub. As I started exploring it, I noticed that there were quite a few dependencies on other npm packages. This got me thinking - is there a way to compile all these files int ...

Dynamically change or reassign object types in TypeScript

Check out the example you can copy and test in TypeScript Playground: class GreetMessage { message: {}; constructor(msg: string) { let newTyping: { textMsg: string }; // reassign necessary this.message = newTyping; this.message.textMsg = msg; ...

Building an AngularJS Service with TypeScript that is Non-Singleton: A Step-by-Step Guide

I need help converting an AngularJS Typescript Service into a Non-Singleton. Can anyone provide guidance on how to achieve this? (Note: This is not the same as other questions that focus on achieving this in JS) I have included some simple pseudo code be ...

Break down a data structure into a type that includes multiple options

Is it possible for Typescript to support type or interface "destructuring" (if that's the right term)? I am attempting to achieve something similar to the code snippet below: type SomeRandomType = { propertyOne: string; propertyTwo: number; ...

Changing the name of a tab within a p-tabview

Setting up a p-tabview with tabs containing specific content involves the following code: <p-tabView class="tabmain" > <ng-container *ngFor="let tab of tabs"> <p-tabPanel [header]="tab.header" > ...

Leveraging parameters within a sequence of object properties

Within the realm of Angular, I am dealing with interfaces that take on a structure similar to this (please note that this code is not my own): export interface Vehicles { id: number; cars: Car; trucks: Truck; } Export interface Car { make: ...

Tips for updating the display after making an angular $http request using rxjs Observables

I have a project where I am utilizing angular's $http service to fetch data from a remote endpoint. I am keen on incorporating rxjs Observables, hence the call in my service is structured as follows: userInfo() : Rx.Observable<IUserInfo> { ...

Combining Vue with Typescript and rollup for a powerful development stack

Currently, I am in the process of bundling a Vue component library using TypeScript and vue-property-decorator. The library consists of multiple Vue components and a plugin class imported from a separate file: import FormularioForm from '@/FormularioF ...

The CSS formatting is not being properly applied within the innerHTML

I have a scenario where I am trying to display a Bootstrap card using innerHTML in my TS file, but the styles are not being applied to this content. I suspect that the issue might be because the styles are loaded before the component displays the card, cau ...

Unlocking the Power of Typescript and ReactJS: Maximizing Efficiency with Previous State

I'm encountering difficulties implementing the previous state in React 18 with Typescript version 4.8.3. Here is my refreshToken code and I'm receiving the following error: Value of type '(prev: any) => any' has no properties in c ...