What is the best way to index an object using a string without widening the object's type?

Take a look at the code snippet below. The function getStatusDescription is designed to return a description for a given HTTP status code if it exists in the predefined list, otherwise it returns the status code itself.

const StatusDescription = {
  200: 'ok',
  400: 'bad request',
  500: 'internal server error',
}

const getStatusDescription(status: string) = {
  return StatusDescription[status] || status
}

An error occurs at the return statement:

Element implicitly has an 'any' type because expression of type 'string' cannot be used to index type '{ 200: string; 400: string; 500: string; }'.

No index signature with a parameter of type 'string' was found on type '{ 200: string; 400: string; 500: string; }'. (7053)

A suggested fix involves setting

StatusDescription: Record<string, string> = …
, but this would extend the key type of the object to include all possible strings, which is not desirable.

Attempting to use

return status in StatusDescription ? StatusDescription[status] : status
results in the same TypeScript error. Therefore, manual type conversion is necessary:

return (
    status in StatusDescription
        ? StatusDescription[status as unknown as keyof typeof StatusDescription]
        : status
)

This solution is quite verbose. A shorter and potentially incorrect alternative approach is:

return (
    StatusDescription[status as unknown as keyof typeof StatusDescription]
        || status
)

Although this does not generate any errors, it raises concerns about whether status truly corresponds to a keyof StatusDescription during the type conversion process.

Is there a more concise solution available (similar to obj[key] || key) that still provides type safety?

Answer №1

If you're looking for a helpful map to simplify the handling of verbose casts, consider using the following:

// Define a Map with string keys and string values
const StatusDescription = new Map(Object.entries({
  200: 'ok',
  400: 'bad request',
  500: 'internal server error',
}));

const getStatusDescription(status: string) = {
  // Simple retrieval function
  return StatusDescription.get(status) ?? status;
}

// Assert that a key exists using '!' for normal access
StatusDescription.get(500)!; 

The ?? operator is known as nullish coalescing, which you can learn more about here.

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

Having trouble compiling the sample JHipster Node.js application

I successfully cloned the jhipster-sample-app-nodejs repository from GitHub (link). Following all the compilation and execution instructions for the application, I have installed the necessary tools (npm, nodejs, yo). However, upon attempting to start th ...

Perform TypeScript type checks exclusively on a Next.js project

In my current project using Next.js with TypeScript in a mono-repo setup, I have multiple applications under the same repository. When pushing changes to this setup, various hooks are triggered locally to ensure that the modifications meet the required sta ...

Accessing attributes declared in the constructor from within class methods is not possible

I am encountering an issue with my HomeController and its index method. I have declared a variable called `this.data` in the constructor, but when I try to access it within the index method, I get the following error message: TypeError: Cannot read proper ...

Unusual output from the new Date() function: it displays the upcoming month

Your assistance and explanation are greatly appreciated. I have created a method that is supposed to return all the days of a given month by using two parameters- the year and the month: private _getDaysOfMonth(year: number, month: number): Array<Date& ...

Prior to the loading of the AppModule in Angular, make sure to load

Consider the following situation (Angular v7): Retrieve configuration settings (API server URL and Auth server URL) from an external source (JSON), prior to loading the AppModule Provide configuration to the AppModule (OAuth2 module) Compile the applicat ...

combineLatest will trigger only for the initial event

I am looking to combine 3 events and trigger a service call when any of them are fired. Currently, I am using the combineLatest method, but it seems to only work when the first event is triggered by filterChanged. The issue here is that filterChanged is a ...

Strategies for setting the output value for a defined generic type

Is there a way to create a function that accepts optional properties common across different types, while also requiring specific properties based on the generic type passed in? type Diff<T, U> = T extends U ? never : T type DiffTypes<T, U> = ...

Webpack is having trouble compiling TypeScript 3.7 due to the inclusion of features such as Optional Chaining and Nullish Coales

Trying to utilize the latest features of TypeScript 3.7 such as Optional Chaining and Nullish Coalescing is causing an error in webpack during transpilation. app: Module parse failed: Unexpected token (50:40) app: File was processed with these loaders: ...

Issue with Angular 9 application: Unable to properly render form fields within a Material Design Dialog

I'm currently developing a "Tasks" application using Angular 9 and PHP. I've encountered an error that says Cannot find control with name: <control name> when attempting to pre-fill the update form with data. Here is the form template: &l ...

How can I show a limited number of columns in a mat-table in Angular 6 depending on a specific condition?

Currently, I am facing an issue with my mat table as it contains several columns. In a specific scenario, I need to hide two of these columns. Typically, for a regular table, we would use a simple ngIf condition to achieve this. However, in the case of thi ...

What are the steps to resubscribe after opting out?

My Angular service has three attributes and two functions: timerSubscription: Subscription; pollingSubscription: Subscription; incrementTimerSubscription: Subscription; async startTimer(idTask: number) { this.timerSubscription = this.folderService . ...

Currently, I am attempting to implement password strength validation using Angular

Looking for a way to implement password strength validation in Angular? You may encounter an error message like this: NullInjectorError: No provider for password strength! in the passwordstrength.ts file HTML <div class="validation_errors t ...

How can I ensure that TypeORM, Type GraphQL, Apollo Server, and Azure Functions work together seamlessly?

I have an Azure Function written in TypeScript that utilizes various technologies such as TypeORM, Apollo Server, and TypeGraphQL. The function involves creating resolvers for projects and tasks and establishing a database connection. import { createConne ...

Creating a stationary Map in Typescript: A step-by-step guide

I am attempting to create a static Map that will store key value pairs with strings only. These values are meant to be constant and never change. Here is an example of what I'm trying to achieve: static KEY_VALUE_PAIR: Map<string, string>: { & ...

Converting Scss to css during the compilation process in Angular

Seeking assistance with .css and .scss file conversion. I am in need of help with generating or updating a .css file from an existing .scss file during compilation. To explain further: when writing code, everything is going smoothly until I decide to save ...

What makes it still feasible to convert any type of variable to "any" in TypeScript?

As I continue to improve my coding skills with TypeScript, I find myself in the shoes of a full stack developer experienced in C#, an object-oriented programming language. Personally, I have a strong distaste for dynamically typed languages and fortunately ...

The data type does not match the expected type 'GetVerificationKey' in the context of express-jwt when using auth0

I am in the process of implementing auth0 as described here, using a combination of express-jwt and jwks-rsa. However, I encountered an error like the one below and it's causing issues with finishing tsc properly. Error:(102, 5) TS2322: Type 'S ...

Switching to esnext has caused Typescript express to stop functioning properly

I've encountered an issue while working on a React TypeScript application. To deal with problems related to 'top level await', I decided to switch my target and module to 'es2017' and 'esnext' respectively. Changing the t ...

What is a typical data transformation pattern used in TypeScript and Angular 2?

Developing an Angular 2+ application using TypeScript. I've been structuring objects for storage in a data source, including helper methods within them. I have successfully saved these objects. ORIGINAL CLASS DESIGN EXAMPLE export class Order { ...

Tips for choosing a value using the index in Angular 12's Reactive Forms

Check out the following code snippet: <select formControlName="test"> <option value="1">A</option> <option value="2"& selected>B</option> <option value="1">C</option> ...