Enhance your Typescript code by incorporating typing for response objects that include both index signatures and key-value pairs

I am grappling with how to properly incorporate typescript typings for the response object received from a backend service:

{
    de49e137f2423457985ec6794536cd3c: {
        productId: 'de49e137f2423457985ec6794536cd3c',
        title: 'item 1',
    },
    d6623c1a2b843840b14c32685c212395: {
        productId: 'd6623c1a2b843840b14c32685c212395',
        title: 'item 2',
    },
    ids: [
        'de49e137f2423457985ec6794536cd3c',
        'd6623c1a2b843840b14c32685c212395',
    ],
}

This response object contains an array of item ids string[] and an index signature [id: string]: Item.

Trying to define this in Typescript is proving tricky because having both the index signature and array together in one interface causes issues. For instance:

interface ItemList {
    [id: string]: Item;
    ids: string[];
}

I understand that when using an index signature, all other properties need to have the same type. As I navigate through Typescript, I am uncertain about how to handle this data structure without extracting the ids out of the item object.

interface ItemList {
    [id: string]: Item;
    ids: string[];
}
interface Item {
    productId: string;
    title: string;
}

const item: ItemList = {
    de49e137f2423457985ec6794536cd3c: {
        productId: 'de49e137f2423457985ec6794536cd3c',
        title: 'item 1',
    },
    d6623c1a2b843840b14c32685c212395: {
        productId: 'd6623c1a2b843840b14c32685c212395',
        title: 'item 2',
    },
    ids: [
        'de49e137f2423457985ec6794536cd3c',
        'd6623c1a2b843840b14c32685c212395',
    ],
};
console.log(item.ids.map((id: string) => item[id]));

Error Message

The property 'map' is not found on type 'Item | string[]'.

The property 'map' is not found on type 'Item'.

Answer №1

To resolve this issue, you can utilize an intersection type:

type ListOfItems = {
    [key: string]: Item;
} & {
    keys: string[];
}
interface Item {
    id: string;
    name: string;
}

const itemsList: ListOfItems = Object.assign({ // You cannot directly create the object
    abc123: {
        id: 'abc123',
        name: 'Item A',
    },
    xyz456: {
        id: 'xyz456',
        name: 'Item B',
    }
}, {
    keys: [
        'abc123',
        'xyz456',
    ],
});
console.log(itemsList.keys.map((key: string) => itemsList[key]));

The intersection type allows for the combination of inconsistent named properties with indexers. (It should be noted that while not strictly type-safe because items['keys'] does not return an Item as expected, it appears to be a fair compromise in this scenario)

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

Issue arose while attempting to use Jest on a React Native application integrated with TypeScript (Jest has come across an unforeseen token)

Seems like everyone and their grandmother is facing a similar issue. I've tried everything suggested on Stack Overflow and GitHub, but nothing seems to work. It should be a simple fix considering my project is basic and new. Yet, I can't seem to ...

What is the right way to import a module in TypeScript?

There is a module that I have declared like so: declare module conflicts { export interface Item {} export interface Item2 {} export interface Item3 {} } I attempted to import this module in a component using the following code: import * from ...

What is the best way to retrieve distinct objects based on LocId across all locations?

Encountering an issue while working on Angular 7: unable to return distinct or unique objects based on LocId. The goal is to retrieve unique objects from an array of objects containing all Locations. allLocations:any[]=[]; ngOnInit() { this.locationsServ ...

Ways to parse the data from a response received from an Axios POST request

After sending the same POST request using a cURL command, the response I receive is: {"allowed":[],"error":null} However, when I incorporate the POST request in my code and print it using either console.log("response: ", resp ...

What is the reason behind create-next-app generating .tsx files instead of .js files?

Whenever I include with-tailwindcss at the end of the command line, it appears to cause an issue. Is there any solution for this? npx create-next-app -e with-tailwindcss project_name ...

Prevent the event listener from continuously triggering

I have a situation where every time I create an Angular component, an event listener is added. However, upon leaving the page and returning to it, a new event listener is added because the constructor is called again. The problem arises when this event is ...

The issue of Angular 9 not recognizing methods within Materialize CSS modals

I am currently working on an Angular 9 application and have integrated the materialize-css 1.0 library to incorporate a modal within my project. Everything works smoothly in terms of opening and instantiating the modal. However, I have encountered an issue ...

Create Functions to Encapsulate Type Guards

Is it possible to contain a type guard within a function as shown below? function assertArray(value: any): void { if (!Array.isArray(value)) { throw "Not an array" } } // This doesn't work function example1(value: string | []) { a ...

ReactNative: When attempting to navigate, a TypeError occurred - this.props.navigation.navigate is not a function

It's confusing to see how this issue is occurring, even though all props have been properly typed. The goal is to pass the navigator to the bottom bar component in order to navigate onPress. Initially, I define the props interface: export interface B ...

Retrieve the data from the mat-checkbox

My goal is to retrieve a value from a mat-checkbox, but the issue is that we only get boolean expression instead of the string value. Here's an example snippet of what I'm looking for: <mat-checkbox formControlName="cb2" <strong&g ...

Aurelia: The passing down of views and view-models

In the process of developing an Aurelia app, I am tasked with creating functionality that allows users to display various lists for different resources. These lists share common features such as a toolbar with search and refresh capabilities, along with a ...

Is there any need for transpiling .ts files to .js when Node is capable of running .ts files directly?

If you are using node version 12, try running the following command: node hello.ts I'm curious about the purpose of installing typescript globally with npm: npm install -g typescript After that, compiling your TypeScript file to JavaScript with: ...

The angular-CLI does not support the use of content delivery networks (CDNs) within the .angular-cli.json

Is there a way to make angular-cli recognize when we add any deployed URLs for styles or scripts using CDN? Currently, adding them to index.html works but adding to .angular-cli.json has no effect. Any workaround available? ...

Exploring the syntax of typescript when using createContext

Just starting out with typescript and I have some questions. Could someone break down the syntax used in this code snippet for me? What is the significance of having two groups containing signIn, signOut, and user here? Is the first group responsible fo ...

The attribute 'attribs' is not found on the 'Element' type in cheerio

When I run my code, I encounter an error that says Property 'attribs' does not exist on type 'Element'. It's puzzling to me why this error is being thrown. After examining the type definitions of cheerio, I discovered that attribs ...

Combine an array of objects that are dynamically created into a single object

Having trouble transforming the JSON below into the desired JSON format using JavaScript. Current JSON: { "furniture": { "matter": [ { "matter1": "Matter 1 value" }, { "matter2": "Matter 2 value" }, { ...

Retrieve all elements within an Angular6 Directive that share the same name as the Directive

I have created a custom Directive called isSelected and applied it to several elements in different components as shown below: <i isSelected class="icon"></i> <i isSelected class="icon"></i> <i isSelected class="icon"></i ...

Transitioning an AngularJS factory to TypeScript

I'm currently in the process of transitioning an AngularJS application to Angular and one of the challenges I've encountered is converting my JavaScript code to TypeScript. While I've been successful with components and services, factories h ...

Updating the Nuxt3 editing page with useFetch and $fetch for fetching data, along with a typed storage object that prevents loading issues

My journey with nuxt3 is still new. I am currently working on developing a new API-based app with nuxt3. Previous versions seemed to work "somehow," but they did not meet my expectations. Here are the things I am looking to cover, both in theory and in cod ...

Display corresponding JSON images of items within an *ngFor loop in Angular

For my latest project, I am using Angular for the front-end and Laravel for the back-end. The issue I'm facing is with displaying images in Angular that are stored in Laravel storage. The image URLs are stored in the database in JSON format like this: ...