TypeScript: "The type is generic and can only be accessed for reading." - Error code 2862

Consider this sample JS function that requires type annotations:

const remap = (obj) => {
  const mapped = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key;
  });

  return mapped;
};

I am attempting to add types using generics (in this TS playground), but I keep encountering the following error:

Type 'Mapped<T>' is generic and can only be indexed for reading.(2862)
type Mapped<T> = {
  [K in keyof T]?: boolean;
};

const remap = <T extends Record<string, unknown>>(
  obj: T
) => {
  const mapped: Mapped<T> = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key; // Type 'Mapped<T>' is generic and can only be indexed for reading.(2862)
  });

  return mapped;
};

I am curious as to why TypeScript does not allow me to write to an object of a generic type, and if there might be another workaround. I expect TypeScript to recognize the type of mapped and grant me permission to write to it, but it seems to restrict that.

Would utilizing as during the return statement be my sole option?

const remapWithAs = <T extends Record<string, unknown>>(
  obj: T
) => {
  const mapped: Record<string, boolean> = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key;
  });

  return mapped as Mapped<T>; // Is this my only choice?
};

Answer №1

The root cause of the error lies in the fact that Object.keys(x) is defined in the TS library to return string[] instead of something like (keyof typeof x)[]. This intentional design choice is elaborated on in this StackOverflow post. Therefore, when accessing mapped[key], you are potentially using a key of type string rather than ensuring it aligns with Mapped<T>. Consequently, directly assigning a boolean value to it might not be safe as you could be modifying a key unknown to Mapped<T>.

It's worth mentioning that TypeScript allows you to retrieve a boolean from mapped[key] despite this being technically precarious, as shown below:

Object.keys(obj).forEach((key) => {
  const test = mapped[key]; // boolean | undefined
});

This is just how TypeScript operates. Thus, the error message stating that Mapped<T> can only be indexed with string for reading purposes. Previously, the message used to prohibit indexing Mapped<T> with string entirely, but this was corrected due to inaccuracies, as documented in microsoft/TypeScript#47357.


If you assert confidently that Object.keys(obj) will yield (keyof T)[] despite TypeScript's concerns about potential additional keys, you can proceed as follows:

const remap = <T extends Record<string, unknown>>(
  initialState: T
) => {
  const mapped: Mapped<T> = {};
  (Object.keys(initialState) as (keyof T)[]).forEach(key => {
    mapped[key] = !!key; // fine
  });

  return mapped;
};

In this context, TypeScipt accepts that mapped[key] conforms to type

Mapped<T>[keyof Mapped<T>]</code — namely, <code>boolean | undefined
— and therefore permits assignments of type boolean.

Link to code playground

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

What could be causing the Properties Array to come back as undefined?

When attempting to add an item to an array stored in the properties, I am encountering an error: "Cannot read properties of undefined (reading 'value')." Within the props, the following interfaces are defined: ILinkItemProps.ts export interface ...

What is the best way to assign JSON values to my class property?

I've been working on a weather application that showcases the current weather of 5 different cities. By clicking on each city, users can access a detailed view displaying the 5-day forecast for that particular location. Currently, I have defined a we ...

What is the best way to organize code within the main.ts file in a Vue 3 project?

New to Typescript and vue, I am eager to figure out how I can extract this code from my main.ts file. I'm concerned about it becoming messy as more icons are added. const app = createApp(App); /* import the fontawesome core */ import { library } from ...

SvelteKit is having trouble with identifying Typescript syntax

I was working on a SvelteKit project with TypeScript (set up with Vite) and everything was running smoothly with "npm run dev". However, when I attempted to publish the app on Github Pages, an error popped up (on localhost) as I hovered over the only link ...

Compiling Typescript tasks in Visual Studio Code - ensuring output encoding is set correctly

Have you tried manually setting up a typescript compilation task in Visual Studio Code? You can find detailed instructions at this link. When you run the build command (Ctrl+Shift+B), are you seeing an error message from tsc with unknown encoding? Check o ...

Bringing in a module that enhances a class

While scouring for a method to rotate markers using leaflet.js, I stumbled upon the module leaflet-rotatedmarker. After installing it via npm, I find myself at a loss on how to actually implement it. According to the readme, it simply extends the existing ...

Why use rxjs observables if they don't respond to updates?

I have an array of items that I turn into an observable using the of function. I create the observable before populating the array. However, when the array is finally populated, the callback provided to subscribe does not execute. As far as I know, th ...

rxjs in Angular2 is missing the observable.interval function

Currently, I am attempting to utilize the interval method of an observable; however, I consistently encounter the following error message: Property 'interval' does not exist on type 'Observable<any>'. I have included the follow ...

The declaration file for the 'react' module could not be located

I was exploring Microsoft's guide on TypeScript combined with React and Redux. After executing the command: npm install -S redux react-redux @types/react-redux I encountered an error when running npm run start: Type error: Could not find a decla ...

Utilizing Typescript for parsing large JSON files

I have encountered an issue while trying to parse/process a large 25 MB JSON file using Typescript. It seems that the code I have written is taking too long (and sometimes even timing out). I am not sure why this is happening or if there is a more efficien ...

Can anyone offer any suggestions for this issue with Angular? I've tried following a Mosh tutorial but it's

Just finished watching a video at around 1 hour and 35 minutes mark where they added the courses part. However, I encountered an error during compilation. ../src/app/app.component.html:2:1 - error NG8001: 'courses' is not recognized as an elemen ...

Typescript not being transpiled by Webpack

As I set out to create a basic website, I opted to utilize webpack for packaging. TypeScript and SASS were my choice of tools due to their familiarity from daily use. Following the documentation at https://webpack.js.org, I encountered issues with loaders ...

Filtering nested arrays in Angular by cross-referencing with a navigation menu

In the legacy application I'm working on, we have a navigation menu along with a list of user roles. Due to its legacy nature, we have accumulated a significant number of user roles over time. The main goal is to dynamically display the navigation me ...

I have attempted numerous methods, but the TypeScript object remains potentially undefined

My current code involves using canvas to capture the cropped image. Below is the function that handles this process: export const getCroppedImg = ( image: HTMLImageElement, crop: Crop, fileName: string ): Promise<Blob> => { let canvas: HTM ...

Using Angular 2 to round a calculated number within HTML

In the HTML code, there is a calculated number associated with Component1. Component1 serves as a tab page within a Bootstrap tab panel. Below is the HTML code with the tab panel: <div id="minimal-tabs" style="padding:75px;padding-top:60 ...

Adding an element to an array does not automatically reflect on the user interface

After fetching JSON data from the endpoint, I am attempting to update an array but not seeing the expected results on the frontend. export class LocationSectionComponent implements OnInit{ myControl = new FormControl(); options : string[] = [' ...

Why can't I omit <someUnion, oneInterface> in TypeScript?

Kindly review this simple example: interface A { a: number; } interface B { b: number; } interface C { c: number; } type ABC = A | B | C; type omitA = Omit<ABC, A>; https://i.sstatic.net/5Mun4.png Unfortunately, I am unable to exclude an i ...

Exciting Update: Next.js V13 revalidate not triggering post router.push

Currently using Next.js version 13 for app routing, I've encountered an issue with the revalidate feature not triggering after a router.push call. Within my project, users have the ability to create blog posts on the /blog/create page. Once a post is ...

The error message states that the property "user" is not found in the type "Session & Partial<SessionData>"

I recently had a javascript code that I'm now attempting to convert into typescript route.get('/order', async(req,res) => { var sessionData = req.session; if(typeof sessionData.user === 'undefined') { ...

Encountering a type-safety problem while attempting to add data to a table with Drizzle

My database schema is structured like so: export const Organization = pgTable( "Organization", { id: text("id").primaryKey().notNull(), name: text("name").notNull(), createdAt: timestamp("c ...