A function in Typescript that can handle dynamic keys and values using generics

function convertArrayToObject<T>(array: T[], key: keyof T): Record<string, T> {
  return array.reduce(
    (accumulator: Record<string, T>, currentValue: T) =>
      Object.assign(accumulator, {
        [String(currentValue[key])]: currentValue,
      }),
    {} as Record<string, T>
  );
};

Is there a way to avoid using "as string"? How can I ensure type checking that the value of property "key" in type T is always a string?

type T {
    someOtherProperty: 20,
    active: false,
   [key]: string; (the value type of the property "key" must be string)
}

Answer №1

To enhance the functionality of arrayToObject(), you can make it generic by allowing both the array element type T and the key type K to be specified, and then constrain T so that its property at key K must be of type string:

const arrayToObject = <T extends Record<K, string>, K extends keyof T>(
  array: T[], key: K
): Record<string, T> => {
  return array.reduce<Record<string, T>>(
    (accumulator, currentValue) =>
      Object.assign(accumulator, {
        [currentValue[key]]: currentValue,
      }),
    {}
  );
};

This will compile without any errors. Testing it out:

interface Bar {
  a: number,
  b: string,
  c: boolean
}

const bars: Bar[] = [
  { a: 1, b: "alpha", c: true },
  { a: 2, b: "beta", c: false },
  { a: 3, b: "gamma", c: true }
];

const result = arrayToObject(bars, "b"); // This is valid

Object.entries(result).forEach(
  ([k, v]) => console.log(k, v.a.toFixed(1), v.b.toUpperCase())
);
/* "alpha",  "1.0",  "ALPHA"
   "beta",  "2.0",  "BETA"
   "gamma",  "3.0",  "GAMMA" */

arrayToObject(bars, "a") // This will produce an error
// ----------> ~~~~
// The types of property 'a' are incompatible.

The code seems to be working fine. The compiler will accept correct calls and flag any incorrect key assignments, providing clear error messages if needed.

Link to code on the TypeScript Playground

Answer №2

Here is a solution:

const arrayToObject = <T extends Record<any, string>>(array: T[], key: keyof T): Record<string, T> => {
  return array.reduce(
    (accumulator: Record<string, T>, currentValue: T) =>
      Object.assign(accumulator, {
        [currentValue[key].toString()]: currentValue,
      }),
    {}
  );
};

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

Why bother with creating mappers to transform entity-to-DTOs?

There are classes referred to as 'mappers' that are utilized by some individuals for converting DTOs to entities or vice versa. What benefits do I stand to gain from employing this technique during backend development? I am keen on delving deepe ...

Troubles encountered while attempting to properly mock a module in Jest

I've been experimenting with mocking a module, specifically S3 from aws-sdk. The approach that seemed to work for me was as follows: jest.mock('aws-sdk', () => { return { S3: () => ({ putObject: jest.fn() }) }; }); ...

Learn how to import from a .storybook.ts file in Vue with TypeScript and Storybook, including how to manage Webpack configurations

I'm currently utilizing Vue with TypeScript in Storybook. Unfortunately, there are no official TypeScript configurations available for using Vue with Storybook. How can I set up Webpack so that I am able to import from another .storybook.ts file with ...

Can you showcase two distinct perspectives on a single page?

One of my components has nested ngFor directives looping through an array of users and their posts. I have a view link within this element, and when clicked, it should show detailed information about both the user and the post. However, the current setup i ...

The TypeScript error message "Type 'x' cannot be assigned to type 'IntrinsicAttributes & IntrinsicClassAttributes<Class>'" was displayed on the screen

I encountered an issue when trying to pass a class method to a child component in a new code. While it worked perfectly in another code, I am now receiving the following error message: view image here What steps should I take to resolve this error? Do I ...

How to Utilize an Array from Observable Object in Angular 2 with ngFor and Async Pipe

I am currently learning about the utilization of Observables in Angular 2. Here is a snippet of code from a service I am working with: import {Injectable, EventEmitter, ViewChild} from '@angular/core'; import {Observable} from "rxjs/Observab ...

organizing strings in alphabetical order using TypeScript

My md-collection is set up to display a list of emails like this: <md-collection-item repeat.for="u of user" class="accent-text"> <div class="row"> <di ...

Forwarding refs in React FC allows you to easily pass down

I have encountered an issue with references - I am trying to reference a function component and pass props to it. Currently, I have my Parent component and Child Component set up. In the parent component, I need to use a ref to access my child component. S ...

Escape from the abyss of callback hell by leveraging the power of Angular, HttpClient, and

I'm currently grappling with understanding Angular (2+), the HttpClient, and Observables. I'm familiar with promises and async/await, and I'm trying to achieve a similar functionality in Angular. //(...) Here's some example code showca ...

Unable to store the outcomes from [ngbTypeahead] in [resultTemplate]

I'm trying to integrate ngbTypeahead into my HTML using the code snippet below <ng-template #rt let-r="result" let-t="term"> <ngb-highlight [result]="r.FirstName" [term]="t"></ngb-highlight> </ng-template> <input name ...

Mapping through multiple items in a loop using Javascript

Typescript also functions Consider an array structured like this const elementList = ['one', 'two', 'three', 'four', 'five'] Now, suppose I want to generate components that appear as follows <div&g ...

Tips for adding temporary text in filter input of Kendo UI Grid using Angular

I'm currently working with Kendo UI Grid in conjunction with Angular, and I am struggling to find a solution for adding text or a placeholder in filter inputs using Typescript. Within my code, I am utilizing the kendoGridFilterCellTemplate: <kend ...

Monitor the change in values upon pressing the submit button on Angular

I am currently working with an edit form that contains data in the input fields. <ng-form #infoForm="ngForm" novalidate> <div> <label for="firstName">First Name :</label> <input type=" ...

Modifying data types within complex nested object structures

I am looking to traverse the data structure recursively and create a custom type with specific fields changed to a different type based on a condition. Using the example structure below, I aim to generate a type (Result) where all instances of A are repla ...

The element within the iterator is lacking a "key" prop, as indicated by the linter error message in a React component

Encountering an error stating Missing "key" prop for element in iteratoreslintreact/jsx-key {[...Array(10)].map((_) => ( <Skeleton variant="rectangular" sx={{ my: 4, mx: 1 }} /> ))} An attempt to resolve this issue was made ...

I'm having trouble retrieving the information as it is showing as undefined. Can anyone offer any advice?

Attempting to extract specific information from an API response has proven challenging. Despite my efforts to isolate the desired data, all listed details appear as undefined. import { HttpClient } from '@angular/common/http'; import { Injectable ...

In Deno, it is possible to confirm that a variable is an instance of a String

I'm having trouble asserting instances of string in Deno: import { assertInstanceOf } from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2642405050405e445e59445e">[email protected]</a& ...

deliver a precise observable

Recently, I spent hours following a tutorial on jwt refresh tokens, only to discover that the code was outdated and some changes were required. As a result, I created an interceptor which encountered an issue with the Observable component, leaving me unsur ...

Error: Module '...' or its type declarations could not be located

Recently, I attempted to deploy my next.js app on Vercel, only to encounter an error that read: "Type error: cannot find module '...' or its corresponding type declarations." After some investigation, I suspect this error is related to local modu ...

How to create a collapse feature that allows only one item to be open at a time in Angular

I developed an angular app with a collapse feature, but I noticed that multiple collapses can be open at once. I am utilizing Ng-Bootstrap collapse for this functionality. Below is the code snippet from the TS file: public isCollapsed = true; And here is ...