TypeScript: Defining a custom object type using an array of objects

Is there a way to dynamically generate an object based on the name values in an array of objects?


interface TypeA {
  name: string;
  value: number;
}

interface TypeB {
  [key: string]: { value: any };
}

// How can we create an OutputType without hard-coding new keys and values?
interface OutputType {
  test: {value: any},
  test2: {value: any},
}

const arrayOfObjectsToObject = (array: TypeA[]):OutputType =>
  array.reduce((acc: TypeB, { name, value }: TypeA) => {
    acc[name] = { value };
    return acc;
  }, {});

const result = arrayOfObjectsToObject([ // {test:{value:1}, test2:{value:2}} etc...
  { name: 'test', value: 1 },
  { name: 'test2', value: 2 }
]);

Answer №1

In order to transform a tuple into the desired object type, we can introduce a generic type called ToOutputType which utilizes a mapped type.

type ToOutputType<T extends { name: string, value: any }[]> = {
    [K in T[number] as K["name"]]: { value: K["value"] }
} 

Furthermore, we can enhance the functionality of arrayOfObjectsToObject by making it generic.

type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

const arrayOfObjectsToObject = 
  <
    T extends { name: K, value: any }[], 
    K extends string
  >(array: readonly [...T]): Expand<ToOutputType<T>> => {
    return array.reduce((acc, { name, value }) => {
      acc[name] = { value };
      return acc;
    }, {} as any) as Expand<ToOutputType<T>>;
  }

The function parameter T represents the tuple input, while K is utilized to narrow down strings within the tuple to literal types. The return type is specified as

Expand<ToOutputType<T>>
. The Expand type is mainly used for aesthetic purposes.


Upon calling the function, you will obtain the following result:

const result = arrayOfObjectsToObject([
  { name: 'test', value: 1 },
  { name: 'test2', value: 2 }
]);

const a = result.test
//    ^? { value: number; }

const b = result.test2
//    ^? { value: number; }

Note that the type of value is inferred as number in both instances. TypeScript automatically broadens the numbers to number, but this behavior can be prevented by using as const.

const result = arrayOfObjectsToObject([
  { name: 'test', value: 1 },
  { name: 'test2', value: 2 }
] as const);

const a = result.test
//    ^? { value: 1; }

const b = result.test2
//    ^? { value: 2; }

Playground


If avoiding the use of as const is preferred, an additional special generic type for inference can be employed.

type Narrowable = string | number | boolean | symbol | object | undefined | void | null | {};

const arrayOfObjectsToObject = 
  <
    T extends { name: K, value: N }[], 
    N extends { [k: string]: N | T | [] } | Narrowable,
    K extends string
  >(array: readonly [...T]): Expand<ToOutputType<T>> => {
    return array.reduce((acc, { name, value }) => {
      acc[name] = { value };
      return acc;
    }, {} as any) as Expand<ToOutputType<T>>;
  }

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

The use of findDOMNode has been marked as outdated in StrictMode. Specifically, findDOMNode was utilized with an instance of Transition (generated by MUI Backdrop) that is contained

I encountered the following alert: Alert: detectDOMNode is now outdated in StrictMode. detectDOMNode was given an instance of Transition which resides within StrictMode. Instead, attach a ref directly to the element you wish to reference. Get more inform ...

Tips for getting Atom cucumber step jump package to function properly on a Windows operating system

Just recently, I installed the Atom text editor along with the cucumber-step package available at this link. However, after pressing CTRL+ALT+j, it failed to jump to the step implementation/definition. My operating system is Windows 10 and I am utilizing ...

Tricks to access value from a Nativescript array of Switch elements when tapping a Button

Scenario: In my project, I am using Nativescript 5.0 with Angular. The data is fetched from an API and displayed in customers.component.ts I am successfully rendering the elements based on the received IDs in customers.component.html When the user inter ...

Unable to find module 'child_process'

Here is the content of my main.ts file: import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { environment } from './environments/environment'; if ...

A versatile method to organize a multi-dimensional array of items

I need help sorting a nested array using a generic function. The sorting should be based on the values of the items within the nested array. Here is an example of my array: type Person = { id: number, name: string, childs: Child[] } type Chil ...

What is the correct way to invoke a function that accepts a combination of specific string values when all I have is a regular string?

Within the TypeScript function declaration provided below, the parameter type alignment consists of unioned literals. function printText(s: string, alignment: "left" | "right" | "center") { // ... } As per the documentation ...

Testing Slack's Web API with Jest for mock purposes

Currently, I am working with two files. One file is where I set up a slack web API client to post a message and test it with a mocked value: main.ts: import { WebClient } from '@slack/web-api'; const slack = new WebClient(process.env.SLACK_API_K ...

I am looking for a guideline that permits me to restrict the use of a form validation tool

We have developed our own version of the Validators.required form-validator that comes with Angular 7, but now we need to switch to using CustomValidators.required. To enforce this change, we are considering banning the use of the old Validators.required b ...

Ways to conditionally display a component in Next.js without the issue of caching CSS styles

I'm a newcomer to Next.js and I'm still trying to wrap my head around how the caching works. Let's take a look at this simplified example: An index page that displays either Test1 or Test2 components, based on whether the current minute is ...

Vercel seems to be having trouble detecting TypeScript or the "@types/react" package when deploying a Next.js project

Suddenly, my deployment of Next.js to Vercel has hit a snag after the latest update and is now giving me trouble for not having @types/react AND typescript installed. Seems like you're attempting to utilize TypeScript but are missing essential package ...

Avoid the import of @types definition without exports in TypeScript to prevent the error TS2306 (not a module)

I have spent a considerable amount of time trying to load a NodeJS library that has what I believe is a faulty type definition in the @types repository. The library in question is geolib and its types can be found in @types/geolib Although I am aware tha ...

Understanding how to handle prop types in a React application using Typescript

My current task involves using the react-google-maps library to integrate Google Maps API into my project. The documentation specifies a certain way to set up the maps component: const GoogleMapComponent: React.ComponentClass<{}> = compose( with ...

Experiencing an array of issues while attempting to convert my HTTP request into an

I am currently facing some difficulties in converting my HTTP requests into observables. Within my Angular App, there is a service called API Service which takes care of handling all the requests to the backend. Then, for each component, I have a separate ...

Activate the button when a checkbox within a looping structure is selected using Angular 5

As a relatively new Angular 5 user, I am working with a button that looks like this: <button *ngIf="type!='invoices' && this.action==='edit'" disabled (click)="genera(fields.termini)" class="ml-16 mat-raised-button mat-accen ...

Guide on saving a PDF file after receiving a binary response using axios

Is there a way to save a PDF file from a binary response received through an axios get request? When making the request, I include the following headers: const config: AxiosRequestConfig = { headers: { Accept: 'application/pdf', respon ...

How can I configure nest.js to route all requests to index.html in an Angular application?

I am developing an Angular and NestJS application, and my goal is to serve the index.html file for all routes. Main.ts File: async function bootstrap() { const app = await NestFactory.create(AppModule); app.useStaticAssets(join(__dirname, '..&ap ...

Changing TypeScript Enum from String to Number in Angular

Does anyone know how to convert a Typescript enum value to a number for display in a dropdown but passing the numeric value on submit? Here is how the enum is currently set up: I am currently able to output the string key of the object when it is emitted ...

What is the process for transforming a multi-dimensional array containing strings into a multi-dimensional array containing numbers?

I've got a unique structure of data composed of arrays with strings as seen below: [ 0: Array(1) 0: Array(6) 0: [5.379856, 43.252967] 1: [5.422988, 43.249466] 2: [5.425048, 43.245153] 3: [5.383804, 43.239 ...

Retrieve data type from std::string in C++

During a recent interview, I was presented with an interesting question. The scenario involved having a function void f(std::string), and calling it with f("int"). The task at hand required the function to create a local variable int x within its body. Th ...

Unable to display toast notification in React/MUI/Typescript project

Currently tackling error notifications targeted at 400,401, and 500 errors within a large-scale project. I am facing an issue where I want to integrate my ErrorToastNotification component into my layout.tsx file to avoid duplicating it across multiple page ...