Indexes in Typescript objects are used to access specific values

While Typescript has been mostly smooth sailing for me, I keep encountering a roadblock.

Let's say I have a type that can be one of several strings:

type ResourceConstant = 'A' | 'B' | 'C';

Now I want to create an object to store the quantity of each resource (essentially a mapping of ResourceConstant to number):

let x: { [key: ResourceConstant]: number } = {};

>>> error TS1337: An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

So what exactly is a "mapped object type"? I've come across information about "mapped types", but it doesn't seem to address this issue directly. Perhaps they suggest using Record:

let x: Record<ResourceConstant, number> = {};

>>> error TS2740: Type '{}' is missing the following properties from type 'Record<ResourceConstant, number>': U, L, K, Z, and 80 more.

Adding Partial seems to make it work:

let x: Partial<Record<ResourceConstant, number>> = {};

It's functional, but definitely not elegant.

Moving on, let's try to apply this somewhere:

for (let res in x) {
    terminal.send(res, x[res]);
}

>>> error TS2345: Argument of type 'string' is not assignable to parameter of type 'ResourceConstant'.

It appears that the type information is lost as objects are always indexed by strings. To resolve this, I'll explicitly specify that it is a ResourceConstant:

for (let res: ResourceConstant in x) {

>>> error TS2404: The left-hand side of a 'for...in' statement cannot use a type annotation.

Perhaps using as could enforce the type:

for (let res as ResourceConstant in x) {

>>> error TS1005: ',' expected

Or maybe casting with the <> syntax?

for (let res<ResourceConstant> in x) {

>>> error TS1005: ';' expected

Neither approach works. It seems I need to introduce a secondary variable to enforce the type:

for (let res in x) {
    let res2 = res as ResourceConstant;
    terminal.send(res2, x[res2]);
}

This solves the issue, but it feels cumbersome.

Despite knowing that switching to new Map would be the correct solution, we're accustomed to using plain objects in JavaScript. Additionally, if working with an existing codebase, should I really have to convert everything to Map just for Typescript? What am I overlooking when dealing with plain objects?

Answer №1

Updates were made in microsoft/TypeScript#26797 regarding allowing index signatures with various property-key types. However, progress on this has been halted due to uncertainties surrounding the compatibility between mapped types and index signature behaviors. This discrepancy arises from the fact that current index signatures exhibit different and less sound behavior compared to mapped types. You may have noticed this when attempting to assign {} to a mapped type with keys being ResourceConstant, only to be denied because of missing properties. While mapped types require all properties to be present, index signatures do not enforce this, leading to unsafe practices. To address this issue, efforts are needed to align mapped types and index signatures for better compatibility. Perhaps the upcoming feature of pedantic index signatures --noUncheckedIndexedAccess could help resolve this? For now, utilizing mapped types remains the solution.

Transitioning from an index signature to a mapped type can be achieved simply by altering

{[k: SomeKeyType]: SomeValueType}
to
{[K in SomeKeyType]: SomeValueType}
. This change mirrors the functionality of the
Record<SomeKeyType, SomeValueType>
utility type
. Additionally, if certain keys need to be omitted, you can make use of Partial<>.

... halt.

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

Difficulty encountered while managing dropdown functionality in Protractor using TypeScript

I'm encountering some difficulties when it comes to selecting a dropdown in Protractor. Here's the structure of my DOM: https://i.stack.imgur.com/qK8sT.png This is the XPath I'm using to select the dropdown with the value "Yes": //label[ ...

Experiencing issues with effectively using a component

I have created a component, but I am unable to use it. This is a problem I have never faced before. https://i.sstatic.net/n5I8V.png Despite extensive Google searches, I have not been able to find a solution. For reference, you can view an example of the ...

There was a serious issue: The mark-compacts were not working effectively near the heap limit, resulting in allocation failure - the JavaScript heap ran out of memory during the

I recently set up a t2.micro server on AWS and encountered an issue when running our application with the command "sudo npm start". The error message I received was: "FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript he ...

Master the art of properly switching on reducer-style payloads in Typescript

Currently, I am dealing with two types of data: GenArtWorkerMsg and VehicleWorkerMsg. Despite having a unique type property on the payload, my Searcher is unable to differentiate between these data-sets when passed in. How can I make it understand and dis ...

What is a more precise way to define an Object type on a temporary basis?

I'm currently working with Angular 2. Typically, when I specify a type, I start by creating an interface: interface Product { name: string, count: number } and then use it like this: let product: Product; However, now I need to temporarily de ...

How do I specify a data type when using a function expression to declare a class?

const Foo: new () => unknown = class { constructor() {} bar(): string { return ‘Hello World!’; } }; const instance = new Foo(); I need to replace 'any' with 'unknown' below due to my configuration settings. new () =&g ...

Using TypeScript to handle text resolution through the command line interface

Currently, I am developing a CLI application using TypeScript and employing enquirer for the purpose. More information about enquirer can be found here. In my project, I have a JSON object defined as follows: const person = { name: 'Mohan', ...

Rearrange component positions without triggering a re-render

I am currently developing a React page for a chat application with a UI design similar to the image provided (Please disregard the black box for sensor info). https://i.sstatic.net/ErVN8.png Within this page, I have created two separate components: The ...

Angular: Clicking on a component triggers the reinitialization of all instances of that particular component

Imagine a page filled with project cards, each equipped with a favorite button. Clicking the button will mark the project as a favorite and change the icon accordingly. The issue arises when clicking on the favorite button causes all project cards to rese ...

Adding a QR code on top of an image in a PDF using TypeScript

Incorporating TypeScript and PdfMakeWrapper library, I am creating PDFs on a website integrated with svg images and QR codes. Below is a snippet of the code in question: async generatePDF(ID_PRODUCT: string) { PdfMakeWrapper.setFonts(pdfFonts); ...

How can I link types from @types to my local code?

I've created a method that utilizes validatorjs for validation purposes. Here's an example of the code: /** * Checks if the string is a mobile phone number (locale options: ['zh-CN', 'zh-TW', 'en-ZA', 'en- ...

What could be causing my matDialog to display incorrectly in Angular version 15?

After I upgraded my project to version 15 of Angular, following the official Angular documentation, I encountered an issue with my MatDialog not opening correctly. The problem seemed to stem from removing the entryComponents and transforming all components ...

How to Send an Email with an Attachment Using an Angular Application

Currently working with Angular9 to build applications. I am looking for guidance on sending emails from an Angular9 application while attaching a CSV file. I would greatly appreciate any sample code you can provide. ...

Errors occurred in Typescript and Next.js due to an unexpected token 'export'

I am currently working on developing an online board app using React, Next.js, and TypeScript as a project for my portfolio. This is my first time using TypeScript. However, I encountered an issue with importing roughjs into canvas.tsx. Below is the code ...

typescript encountered an issue when trying to import the p-retry library

After searching through numerous posts, I have yet to find a solution for the specific scenario involving p-retry. The library provides its own type definitions and updated sample documentation for the latest version. However, when I try to import it using ...

Working with Angular 4: Utilizing HttpResponse in a Component

I am attempting to retrieve the response from my POST request using Angular 4. Below is the code I am using: app.component.html: `findAccordiSmall(pagination: Pagination) { this.accordiListTableLoading = true; debugger; this.ac ...

Mistakes in Compiling Typescript Code in Angular 2

Currently, I am utilizing Visual Studio 2017 for the development of an Angular 2 application with an Asp.Net Core WebApi backend. My guide through this process is the ASP.NET Core and Angular 2 Book authored by Valerio De Sanctis. Initially, everything was ...

Typescript now supports recursive template literal types, enabling a combination of string and specific chaining

Minimal, Complete, and Verifiable Example (MCVE) Check out the live demo here (try using the autocomplete on the parameter of the function f to see what it achieves) I am currently working on creating a recursive template literal to validate if the stri ...

I am curious about the significance of the "=>" symbol within the Ionic framework

I utilized the documentation provided on the Ionic website to incorporate Firebase into my mobile application. this.firebase.getToken() .then(token => console.log(`The token is ${token}`)) // store the token server-side and utilize it for sending not ...

What is the best way to import modules in Typescript/Javascript synchronously during runtime?

I have a Typescript class where I am attempting to perform a synchronous import, however, the import is being executed asynchronously. My code snippet looks like this: --------------100 lines of code-------------------- import('../../../x/y/z') ...