Convert an array of objects into a singular object while preserving data types in TypeScript

I have an array filled with objects containing keys name and value. My goal is to merge these objects into a single object, where the keys correspond to the name property and the values correspond to the value property of the input objects.

type Input = { name: string, value: any }[]
type Output = Record<string, any> // Key-value object { [name]: value }

const input: Input = [
    { name: 'name', value: 'Michal' },
    { name: 'age', value: 24 },
    { name: 'numbers', value: [4, 7, 9] }
]

const getOutput = (input: Input): Output => {
    return input.reduce((output, record) => ({ ...output, [record.name]: record.value }), {})
}

// The output is: ​{ name: 'Michal', age: 24, numbers: [4, 7, 9] } 
const output: Output = getOutput(input)

The provided example works correctly, but it uses the Record<string, any> type for the output. This results in loss of type information for the values. Is there a way to achieve this transformation while preserving the types?

output.age.length // This should trigger a TypeScript error as `number` does not have a `length` property
output.numbers.length // Should be 3
output.address // This should result in a TypeScript error as `input` does not have an `address` property

Answer №1


type Elem<V> = { name: string, value: V }

type Callback<Item> =
    Item extends { name: infer Name, value: infer Value }
    ? Name extends PropertyKey
    ? Record<Name, Value> : never : never


type Reducer<T extends Array<any>, Acc = {}> =
    T extends []
    ? Acc
    : T extends [infer Head, ...infer Tail]
    ? Reducer<Tail, Acc & Callback<Head>>
    : never

const getOutput = <
    N extends number,
    Value extends number | string | [N, ...N[]],
    Name extends string,
    Item extends { name: Name, value: Value },
    Input extends Item[]
>(input: [...Input]) =>
    input.reduce((output, record) =>
        ({ ...output, [record.name]: record.value }),
        {} as Reducer<Input>
    )

const result = getOutput([
    { name: 'name', value: 'Alex' },
    { name: 'age', value: 30 },
    { name: 'items', value: [1, 2, 3] }
])
result.age // 30
result.name // 'Alex'
result.items // [1, 2, 3]

Playground

Explanation

Reducer and Callback - operate similarly to Array.prototype.reducer, but with recursive iterations. Here is a JavaScript representation of Reducer:


const Callback = (elem) => {
    const { name, value } = elem;
    return { [name]: value }
}

const reducer = (arr: ReadonlyArray<any>, result: Record<string, any> = {}): Record<string, any> => {
    if (arr.length === 0) {
        return result
    }

    const [head, ...tail] = arr;

    return reducer(tail, { ...result, ...Callback(head) }
}

Refer to this answer and my blog for more details.

[...Input] - I utilized variadic tuple types for inferring each object in the array

Answer №2

It is important to reevaluate the structure of your input data as it may be causing unexpected results. Consider refining how you manage and process your data for better outcomes.

To address this issue without altering the original input, one approach could be:

const ValueDescriptor = {type:'NUMBER',value:number} | {type:'STRING',value:string} | {type:'NUMBER_ARRAY',value:number[]} /* | ... any custom type*/

type OutputData = Record<string, ValueDescriptor>

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

Update the appearance by utilizing ngModelChange

Is it possible to style an element using ngModelChange? I attempted the following method, but it was unsuccessful <input class="input" [(ngModel)]="counter" (ngModelChange)="$event > 2 ? [style.border-color]='#ff4d4d' : [style.border-color ...

When using http.get(), both Promise and Observable may encounter failures

I am facing an issue with my two simple services that should both return results from a REST API. Initially, I started with using Promises but encountered a problem where toPromise() was not found, similar to the issue discussed here. Then, I attempted t ...

A guide on implementing a Type Guard check for an Indexed Property

I am working with a nestedObj type that utilizes an indexed signature like this: type nestedObj = {[key: string]: nestedObj} | {[key: string]: number} How can I go about creating a type guard for this nestedObj type? const isNestedObj = (obj: any): obj ...

Can discriminated unions solely be utilized with literal types?

When looking at the code snippet below, I encountered an issue with discriminating the union type using the typeof operator. function f(arg: { status: number; one: boolean } | { status: string; two: boolean }) { if (typeof arg.status === "number&q ...

Having trouble establishing a connection to the TyreORM Module database while using a Postgres Docker image

I am currently developing a NestJS Application and have integrated TypeORM to connect with my local database. The database is a postgres image running in the background. Although I am able to connect to the image using pgAdmin, I am encountering an error ( ...

Implement a T3 App Redirect in a TRPC middleware for unsigned users

Is there a way to implement a server-side redirect if a user who is signed in has not finished filling out their profile page? const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { if (!ctx.session || !ctx.session.user) { throw new TRPCE ...

Is it possible to utilize types as constants in a switch statement?

In my file called checkoutTypes.ts, I have defined some checkout types like this: export type CheckoutInvoiceAddressSection = "InvoiceAddress"; export type CheckoutDeliveryAddressSection = "DeliveryAddress"; export type CheckoutDelivery ...

TypeScript: Despite declaring specific types, generic functions still treat parameters as "any"

When using TypeScript 4.4.3, I am looking to specify the types of function parameters for a function that returns a generic. However, TypeScript seems to be treating the parameters as any when working with functions that involve generics. Here's a si ...

The Angular 4 directive appears to be showing 'undefined' when accessing @input values

I am a beginner when it comes to using Angular directives, so I created a directive like this: import { Directive, ElementRef, Input, Output } from '@angular/core'; @Directive({ selector: "[bonusCard]" }) export class BonusCard { @Input() b ...

Implementing a more efficient method for incorporating UUIDs into loggers

------------system1.ts user.on('dataReceived',function(data){ uniqueId=generateUniqueId(); system2.processData(uniqueId,data); }); ------System2.ts function processData(u ...

Can Angular ping local addresses in a manner similar to using the terminal?

Looking for a way to ping devices on my local network, similar to using the ping command in the terminal to ping the router or any connected devices. I've figured out how to ping servers like google.com, but it doesn't seem to work with local add ...

The element 'mdb-range-input' is not recognized. To ensure that it is a valid Angular component, please confirm that it is included in this module

Trying to implement a md bootstrap slider using the following code: <mdb-range-input (rangeValueChange)="onRangeValueChange($event)" id="range" min="0" max="100"></mdb-range-input> The module imports section includes: MDBBootstrapModule.forR ...

Utilizing TypeScript Partials: Efficiently transferring selected fields between objects

I'm currently developing a feature that fetches a list of products from an E-commerce API and I want to enhance it by allowing users to request specific fields from the products while eliminating any unnecessary ones. This is the snippet of code in q ...

Arranging a list of objects in Angular 6

I am facing difficulties in sorting an array of objects The structure of the object is as follows: https://i.sstatic.net/z5UMv.png My goal is to sort the *ngFor loop based on the group_id property. component.html <ul *ngFor="let list of selectgi ...

Defining RefObject effectively in TypeScript

Greetings everyone, I am a newcomer to TypeScript and currently attempting to create a type for a RefObject that is of type HTMLAudioElement. However, I have encountered an error message. The error states: Type 'MutableRefObject<HTMLAudioElement> ...

After compiling typescript, ES6 Map.forEach is unexpectedly not a function

Exploring the new collection types introduced in ES6 for my TypeScript/React project. interface MyComponentProps{ myMap: Map<String, {isAvailable?: boolean}>, } ... this.props.myMap.keys(); Even though IntelliJ and Webpack compile the code withou ...

Translate JSON to TypeScript class. If the property is an object and not present in the JSON, the class should contain an empty object

I am having trouble finding an appropriate title for the issue I am currently facing. Therefore, I will provide a detailed explanation of the problem. I have a class named Model.ts export class Model{ a:ObjA; b:ObjB; c:ObjC; d:string; ...

Tips for utilizing ngIf based on the value of a variable

Here is the code from my file.html: <button ion-button item-right> <ion-icon name="md-add-circle" (click)="save();"></ion-icon> </button> The content of file.ts is: editmode = false; I am trying to achieve the foll ...

Issue during Firebase production: emptyChildrenSingleton isn't recognized

In my nextjs project, I am using React v18.1.0 and Firebase Realtime Database for handling notifications. The notifications work fine in development mode but fail in the production environment. The errors I encountered are as follows: ReferenceError: empt ...

An issue arising with the TypeScript antlr4ts listener type

I am currently attempting to incorporate the antlr4 parser into an angular project. Within a dataservice class, there is a function being called that appears as follows: parseRule() { const ruleString = ' STRING TO PARSE'; const inputS ...