Learn how to create a versatile TypeScript function that combines an array parameter and values to form an object

I've created a function that combines an array of keys with an array of values to form an object. Here's how the function looks:

function mergeToObject(keys: string[], values: string[]) {
  const object:? = {}
  for (let i = 0; i < keys.length; i++) {
    object[keys[i]] = values[i] // TypeScript raises an error here
  }
  return object
}

To demonstrate how the function works, consider this example:

mergeToObject(['a', 'b', 'c'], ['x', 'y', 'z']) // => { a: 'x', b: 'y', c: 'z' }

I always provide the keys parameter as a constant like ['a', 'b', 'c'], rather than a dynamic value. Therefore, I believe there must be a generic way to specify the return type containing specific keys such as a, b, and c.

For better understanding, let's look at a more concrete example:

const values = ['x', 'y', 'z']

// If I call the function like this
const object = mergeToObject(['a', 'b', 'c'], values)
// Then the type of object should be `{ a: string, b: string, c: string }`
object.a // is valid
object.b // is valid
object.c // is valid
object.d // is not valid

// Alternatively, if I call the function like this
const object = mergeToObject(['e', 'f', 'g'], values)
// Then the type of object should be `{ e: string, f: string, g: string }`
object.e // is valid
object.f // is valid
object.g // is valid
object.d // is not valid

So, what exactly would the generic syntax look like? Any guidance on this matter would be highly appreciated.

Answer №1

Utilize the Record method:

function convertToRecord(keys: string[], values: string[]) : Record<string, any> {
  const data: Record<string, any> = {}
  for (let i = 0; i < keys.length; i++) {
    data[keys[i]] = values[i] // TypeScript might give an error here
  }
  return data
}

For further details, check this out: https://www.typescriptlang.org/docs/handbook/utility-types.html#example-3

Please note: I have used any as the type for the values, but you can switch it to string if preferred.

Answer №2

METHOD 1

You can define an interface and assign it like this:

interface combineObject {
   [key: string]: string
}

Here, combineObject represents an object with keys and values as strings.

interface combineObject {
   [key: string]: string
}

function mergeToObjKeys(keys: string[], values: string[]): combineObject {
   const obj: combineObject = {}
   for (let i = 0; i < keys.length; i++) {
      obj[keys[i]] = values[i] // TypeScript error occurs here
   }
   return obj
}

const result = mergeToObjKeys(['a', 'b', 'c'], ['x', 'y', 'z']) // => { a: 'x', b: 'y', c: 'z' }
console.log(result)

METHOD 2

interface combineObject {
   [key: string]: string;
}

function mergeToObjReduce(keys: string[], values: string[]): combineObject {
   return keys.reduce((acc: combineObject, curr: string[], index: number) => {
      acc[curr] = values[index];
      return acc;
   }, {});
}

const result = mergeToObjReduce(['a', 'b', 'c'], ['x', 'y', 'z']); // => { a: 'x', b: 'y', c: 'z' }
console.log(result);

Answer №3

A potential solution to the issue can be represented in the following way:

function convertToEntity<
  Keys extends string[], 
  Values extends any[],
  ResultType extends { 
    [Index in keyof Keys as Keys[Index] extends string ? Keys[Index] : never]: Values[Exclude<Index, number> & keyof Values] 
  }
>(keys: readonly [...Keys], values: readonly [...Values]): ResultType {

  const entity: Record<string, any> = {}
  for (let i = 0; i < keys.length; i++) {
    entity[keys[i]] = values[i]
  }
  return entity as ResultType
}

The arrays containing the keys and values are stored in the generic tuple types Keys and Values. The function's return type is captured in ResultType. When dealing with ResultType, we iterate over Keys and use Keys[Index] as the key name for each property. Values[Index] serves as the type for each property. Due to TypeScript's indexing limitations, we need to utilize this syntax:

Values[Exclude<Index, number> & keyof Values]
.

To verify its functionality, a few tests were conducted:

const entityA = convertToEntity(['alpha', 'beta', 'gamma'], ['red', 42, new Date()])
//    ^? const entityA: { alpha: string; beta: number; gamma: Date; }

entityA.alpha
entityA.beta
entityA.gamma
entityA.delta // Error: Property 'delta' does not exist on type '{ alpha: string; beta: number; gamma: Date; }'

const entityB = convertToEntity(['one', 'two', 'three'], [42, 'blue', null])
//    ^? const entityB: { one: number; two: string; three: null; }

entityB.one
entityB.two
entityB.three
entityB.delta // Error: Property 'delta' does not exist on type '{ one: number; two: string; three: null; }'

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 are some methods for utilizing the "name" attribute within React components?

About My Coding Environment Utilizing TypeScript and ReactJS The Issue with Using name as an Attribute Encountering the following error: Type '{ name: string; "data-id": string; "data-type": string; }' is not assignable to ...

Changing dates in JavaScript / TypeScript can result in inaccurate dates being displayed after adding days

Recently, I encountered an issue with a simple code snippet that seems to produce inconsistent results. Take a look at the function below: addDays(date: Date, days: number): Date { console.log('adding ' + days + ' days'); con ...

Tips for resolving the ExtPay TypeError when using Typscript and Webpack Bundle

I am currently trying to install ExtPay, a payment library for Chrome Extension, from the following link: https://github.com/Glench/ExtPay. I followed the instructions up until step 3 which involved adding ExtPay to background.js. However, I encountered an ...

Is it possible to configure npm to publish to an organization different from the one automatically detected from package.json?

We are looking to implement a process in our open source project where all Pull Requests will be published to npm using CI/CD. To reduce the potential for supply chain attacks, we aim to deploy to a separate organization. Can this be achieved without makin ...

Achieving selective exclusion of specific keys/values while iterating through an array and rendering them on a table using Angular

Currently facing a hurdle and seeking advice I am developing an angular application that fetches data from an API and presents it on the page The service I am utilizing is named "Api Service" which uses HTTPClient to make API calls apiservice.service.ts ...

Issue: The code is throwing an error "TypeError: Cannot read property 'push' of undefined" in the JavaScript engine "Hermes

Can anyone assist me with filtering an array of objects in a TypeScript React Native project using state to store array values and filter objects in the array? Having trouble with the following error in the mentioned method: LOG after item LOG inside ...

Error: Module './App' not found in webpack module

I am encountering the error Uncaught Error: Module not found: Can't resolve ./App' and ./store in client/src. in the console of my local environment when I execute npm start from the client directory. The console is showing 2 errors: ERROR in ...

Problem with rendering React Router v4 ConnectedRouter on nested routes

The routes for the first level are correctly displayed from Layout.tsx, but when clicked on ResourcesUI.tsx, the content is not rendered as expected (see code below). The ResourceUI component consists of 2 sections. The left section contains links, and th ...

There are no call signatures available for the unspecified type when attempting to extract callable keys from a union

When attempting to write a legacy function within our codebase that invokes methods on certain objects while also handling errors, I encountered difficulty involving the accuracy of the return type. The existing solution outlined below is effective at cons ...

Error message: Unable to instantiate cp in Angular 17 application while building with npm run in docker container

After creating a Dockerfile to containerize my application, I encountered an issue. When I set ng serve as the entrypoint in the Dockerfile, everything works fine. However, the problem arises when I try to execute npm run build. Below is the content of my ...

What is the best way to perform a conditional check and return a customized object while working with a Promise?

I have developed a provider specifically for handling all Firebase database related requests. In the getUser method, when the data is fetched from the database using .once which returns a promise, if the object is null it currently returns null. This means ...

Using Angular and Typescript to implement a switch case based on specific values

I am attempting to create a switch statement with two values. switch ({'a': val_a,'b': val_b}){ case ({'x','y'}): "some code here" break; } However, this approach is not functioning as expected. ...

Spread operator in Typescript for complex nested collection types

I have implemented a Firestore database and defined a schema to organize my data: type FirestoreCollection<T> = { documentType: T; subcollections?: { [key: string]: FirestoreCollection<object>; }; }; type FirestoreSchema< T exte ...

Enhancing Your C# Code with Custom Extension Methods

I am currently working on developing a few extension methods that will be applied to various collections, specifically those that implement IList and array collections []. Whenever I create an extension method, I find myself needing to create two versions ...

Utilizing Sequelize to search for existing items or create new ones in a list

My experience with sequelize is limited and I am having trouble understanding certain aspects. Despite my efforts to search for more information, I couldn't find anything specific that addresses my confusion. // API Method function SeederApi(req: Req ...

Utilizing server-side caching middleware with tRPC version 10

Currently, I am working on a Next.js project and exploring the possibility of incorporating in-memory caching for tRPC results. Each tRPC procedure should have the option to set a custom TTL for caching purposes. My initial thought is that utilizing tRPC&a ...

Unable to load custom package in Angular 2 testing environment

I've been following the official Angular 2 testing guide on an existing project. Everything runs smoothly when I use my custom library, downloadjs, in the application. However, I encounter an error in the console during test execution: "__zone_symbol ...

Attempting to send numerous identifiers in an API request

I encountered a problem while working on a function in Angular that involves pulling data from an API. My goal is to enhance a current segment to accommodate multiple IDs, but I face difficulties when attempting to retrieve more than one ID for the API que ...

Exploring JSONPath in Cypress

I am currently working on extracting a JSON path for the specific HTML content with the language code DE Below is an example of the JSON data: { "name": "Name", "text": "", "html": "HTML content" ...

Structuring your Angular 6 application and server project

What is the recommended project structure when developing an Angular 6 application and an API server that need to share type definitions? For example: On the client side: this.httpService.get<Hero[]>(apiUrl + '/heroes') On the server si ...