Issue with Typescript when attempting to create an array of objects with varying keys using the map method

My goal is to dynamically create an array of objects, each potentially containing different keys but always following the same format as defined on myType. However, Typescript throws an error when I attempt to do so. Below is a simplified example.

type myType = {
  [key: string]: number;
};

const doesNotComplain: myType[] = [{ a: 1 }, { b: 2 }];

const doesComplain: myType[] = [1, 0, 0, 0, 1, 0].map((e) => {
  if (e == 1) return { a: 1 };
  return { b: 2 };
});

The error specifically occurs with doesComplain.

Type '({ a: number; b?: undefined; } | { b: number; a?: undefined; })[]' is not assignable to type 'myType[]'.
  Type '{ a: number; b?: undefined; } | { b: number; a?: undefined; }' is not assignable to type 'myType'.
    Type '{ a: number; b?: undefined; }' is not assignable to type 'myType'.
      Property 'b' is incompatible with index signature.
        Type 'undefined' is not assignable to type 'number'.

Things I've attempted:

I have looked at solutions for similar issues, but none seem to fit my specific case.

One solution mentioned here requires a fixed amount of values in the array, which doesn't match what I need.

Other suggestions propose defining myType with number | undefined, but I prefer not to do this since I expect all keys to have defined values.

The only way I found to eliminate the error so far is to use // @ts-ignore above doesComplain, which defeats the purpose of using Typescript.

Answer №1

The simplest and most secure solution in this scenario is to avoid relying on TypeScript's inferred type for the value returned by the map callback. Instead, explicitly specify the return type of the callback like this:

const x: MyType[] = [1, 0, 0, 0, 1, 0].map(
  (e): MyType => e == 1 ? { a: 1 } : { b: 2 }
);

After adding the return type annotation, you don't need to annotate the variable x separately. The type MyType[] will be inferred automatically:

const x = [1, 0, 0, 0, 1, 0].map(
  (e): MyType => e == 1 ? { a: 1 } : { b: 2 }
);

Using annotations is preferred over type assertions because they provide better type safety. Annotations catch errors that type assertions might miss:

const x = [1, 0, 0, 0, 1, 0].map(
  (e): MyType => e == 1 ? { c: "oops" } : { b: 2 } 
);

const x = [1, 0, 0, 0, 1, 0].map(
  (e) => (e == 1 ? { c: "oops" } : { b: 2 }) as MyType
);

This issue originates from TypeScript's handling of object literal types, which was introduced in TypeScript 2.7.


It's unlikely that TypeScript will directly address this issue. However, you can work around it by explicitly annotating expected return types or by returning existing objects rather than fresh object literals.

Click here for a Playground link to view the code

Answer №2

I recently encountered a similar issue and spent countless hours trying to comprehend the situation.

Firstly, let's examine your .map() outcome without specifying the variable.

const untypedMappedResult = [1, 0, 0, 0, 1, 0].map((e) => {
  return (e === 1) ? { x: 1 } : { y: 2 };
});

/*
const untypedMappedResult: ({
    x: number;
    y?: undefined;
} | {
    y: number;
    x?: undefined;
})[]
*/

As you can observe, the result is different from what you might anticipate. Both objects in the combination have two attributes.

To resolve this issue, simply insert a type assertion...

const typedMappedResult = [1, 0, 0, 0, 1, 0].map((e) => {
  return ((e === 1) ? { x: 1 } : { y: 2 }) as myType;
});

/*
const typedMappedResult: myType[]
*/

Note that I didn't need to specify the variable type; it was automatically inferred.

Sample 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

Increase progress by pressing a button

I've implemented the NgCircleProgressModule library to display a circle with a percentage value in the center. It seems that the progress value remains static and only updates when the buttons are clicked. Clicking on the 25% button within the circl ...

Navigating the terrain of multiple checkboxes in React and gathering all the checked boxes

I am currently developing a filter component that allows for the selection of multiple checkboxes. My goal is to toggle the state of the checkboxes, store the checked ones in an array, and remove any unchecked checkboxes from the array. Despite my attemp ...

utilize TypeScript to iterate through an array in a JSON object

How can I loop through the subjects array in a JSON response? JSON Response: { "$id": "1", "Council_ID": 116, "number": "67", "subjects": [ { "$id": "2", "subjectCode": "67", "type": 4, ...

Utilizing raw queries in TypeORM with NestJS to enforce lowercase column names

Can anyone help me execute a query using nest/typeorm? I'm utilizing Typeorm's "InjectConnection" to run a raw query in my Postgres Database. The issue arises with the column user_roles_role.userId (note that I am specifying 'userId' i ...

Angular Lifecycle Hook - Data loading initializes after the view initialization is complete

In my component, I have loaded a firestore document and converted it into a plain js object within the constructor. However, when trying to access the field values in the template, there is a slight delay in loading them. This results in an error being dis ...

Tips for finalizing a subscriber after a for loop finishes?

When you send a GET request to , you will receive the repositories owned by the user benawad. However, GitHub limits the number of repositories returned to 30. The user benawad currently has 246 repositories as of today (14/08/2021). In order to workarou ...

Testing Angular 11 methods using Jest unit tests within a .then block

Currently, I am running jest unit tests in Angular 11 and facing an issue while testing methods within the .then method. It seems like the test methods are not being executed at the expect method. I need guidance on how to structure the test code to ens ...

Improving type definitions in Typescript 2.6 using module augmentation leads to error TS2339: Property '' is not found on type ''

Currently utilizing the material-ui library version v1.0.0-beta. An update was released yesterday to v1.0.0-beta.28, however, the type definitions were not updated resulting in runtime errors while compilation remains successful. Encountering this error i ...

Dev error occurs due to a change in Angular2 pipe causing the message "has changed after it was checked"

I understand the reason for this error being thrown, but I am struggling with organizing my code to resolve it. Here is the problem: @Component({ selector: 'article', templateUrl: 'article.html', moduleId: module.id, di ...

``Should one prioritize the use of Generics over Inheritance, or is there a better way

We are currently in the process of implementing new contracts for our icons system, and we have encountered a debate on which approach is more preferable. Both options result in the same interface: Using Generics -> Although the interface may be less ...

Struggling to fix TypeScript error related to Redux createSlice function

Here is the code snippet I am working on: import { Conversation } from "@/types/conversation"; import { PayloadAction, createSlice } from "@reduxjs/toolkit"; const initialState: Conversation | null = null; export const conversationSli ...

Encountering a console error while attempting to navigate to the 404 page on Angular

I am working on implementing a route to a page in Angular for handling incorrect URL addresses. Error Received in Console While there is no error message in my IDE, the console displays the following error: ERROR TypeError: Cannot read property 'name ...

What is the process for configuring vue.config.js with typescript?

Just starting out with typescript and running into an issue while configuring vue.config.js const webpack = require("webpack"); module.exports = { plugins: [ new webpack.DefinePlugin({ __VUE_I18N_FULL_INSTALL__: true, __ ...

Provide an immutable parameter to a function that will not cause any changes

Looking to develop a function named batchUsers, requiring a parameter of type readonly string in order to create a DataLoader. However, when calling the User.findBy function within my batchUsers function, it's causing issues due to conflicting paramet ...

Loop through the array while handling a promise internally and ensure completion before proceeding

I am currently working on populating a response array with Firestore snapshots and creating download links for stored files within each snapshot. Despite trying various solutions involving Promises, the response array consistently ended up being null. do ...

Utilizing declaration files in a TypeScript application to provide global exposure of types

Recently, I've delved into the world of typescript and set up a project with numerous React components written in typescript. To streamline development, we decided to extract all necessary types for these components into a separate file named types.d. ...

Developing a Javascript object using Typescript

Trying my hand at crafting a TypeScript object from JavaScript. The specific JavaScript object I'm attempting to construct can be found here: https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js In the provided JavaScript example, the obj ...

The compatibility issue between ComponentPropsWithRef and React Native on a pnpm monorepo is causing issues

When I try to use ComponentPropsWithRef with a React Native Button or View in the following way: export type Props = React.ComponentPropsWithRef<typeof Button> & { children?: React.ReactNode } I encounter the following type errors: Type &apo ...

Setting a default value for props in a functional component in NextJS using Typescript when not explicitly passed

Here is the component: const defaultDesc = "welcome to homepage"; export default function Header(Props: { title:string, desc?:string }): JSX.Element { } If no desc is passed in <Header>, I want to set it to t ...

Angular 13: Issue with displaying lazy loaded module containing multiple outlets in a component

Angular version ^13.3.9 Challenge Encountering an issue when utilizing multiple outlets and attempting to render them in a lazy module with the Angular router. The routes are being mapped correctly, but the outlet itself is not being displayed. Sequence ...