Shifting a collection of dictionaries using a fixed text value

My scenario involves three variables with the same type:

const foo = { name: "foo", age: 12, color: "red" } as const;
const bar = { name: "bar", age: 46, color: "blue" } as const;
const baz = { name: "baz", age: 52, color: "green" } as const;

I place these variables into an array like this:

const arr = [foo, bar, baz];

The goal is to transform this array into a dictionary based on name as a unique identifier, where the "name" becomes the key. It's important to maintain the keys as string literal types for further property usage like so:

const dict = arrayToDict(arr);
// `dict.foo` is a known key of `dict`
console.log(dict.foo.age);

Fortunately, there is a function that achieves this in my case. Here it is:

export function arrayToObjectName<T extends { name: S }, S extends PropertyKey>(
  arr: readonly T[]
) {
  return arr
    .reduce((acc, v) => ({ ...acc, [v.name]: v }), {} as { [V in T as V["name"]]: V });
}

However, I aim to make this function more generic by allowing dynamic property selection within the function. Here is my attempt:

function arrayToObject<S extends PropertyKey>(prop: S) {
  return <T extends { [prop]: S }>(
    arr: readonly T[]
  ) => {
    return arr
      .reduce((acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V });
  };

Unfortunately, this approach encounters two issues. The first error pertains to the second function signature (see error):

A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.

Additionally, the assignment V[S] at the end triggers the following error:

Type 'V[S]' is not assignable to type 'string | number | symbol'

Interestingly, despite these type errors, the function still functions correctly!

If you have any insights on how to create a generic function that preserves types successfully, please share. The TypeScript Playground link below showcases both functions and relevant type tests.


An alternative generic implementation has been developed, which also works but has one persistent error:

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

function arrayToObject<
  S extends PropertyKey>(prop: S) {
  return <N extends Narrowable, T extends Record<keyof T, N> & Record<S, T[S]>>(
    arr: readonly T[]
  ) => {
    return arr.reduce(
      (acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V }
    );
  };
}

The recurrent error is linked to V[S], however, the new type ensures that S must be a keyof T. This resolves the initial error encountered.

Answer №1

Indeed, my "alternative approach" came very close to the solution, but a slight adjustment was necessary. Here is the revised implementation:

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

export function arrayToObject<S extends PropertyKey>(prop: S) {
  return <N extends Narrowable, T extends Record<keyof T, N> & Record<S, any>>(
    arr: readonly T[]
  ) => {
    return arr.reduce(
      (acc, v) => ({ ...acc, [v[prop]]: v }), {} as { [V in T as V[S]]: V }
    );
  };
}

This revised version now functions as intended; for example, if you run the following code:

const foo = { name: "foo", age: 12, color: "red" } as const;
const bar = { name: "bar", age: 46, color: "blue" } as const;
const baz = { name: "baz", age: 52, color: "green" } as const;

const dict = arrayToDictionary("name")([foo, bar, baz]);

You will obtain dict, which is a meticulously typed dictionary with keys like foo, bar, and baz. Each key encapsulates the comprehensive type-literal definitions of their corresponding objects.

If there is an even more efficient or concise solution available, I am open to accepting it over my own.

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

Setting up the vscode launch configuration to enable debugging on the cloud-run emulator with TypeScript

I am currently facing an issue with debugging a Google Cloud Run application on the Cloud Run emulator. The application is built using TypeScript. While I can successfully run and debug the application locally, breakpoints are being ignored or grayed out w ...

The TypeScript inference feature is not functioning correctly

Consider the following definitions- I am confused why TypeScript fails to infer the types correctly. If you have a solution, please share! Important Notes: * Ensure that the "Strict Null Check" option is enabled. * The code includes c ...

Assignment of type 'Object' is incompatible with type in new HttpClient / HttpGetModule implementation within Angular

After following the angular tutorial, I decided to test out the new httpClient.Get method. However, it seems that no matter what, it always returns results of type Object. // HttpClient getHeroes2 () { this.http.get<Hero[]>(this.heroesUrl) ...

What is the best method for incorporating the three.js GLTFExporter into a TypeScript project?

I have been exploring the use of GLTFExporter in a three.js project written in TypeScript. I started with a basic template from https://github.com/pinqy520/three-typescript-starter and made modifications to the render function as follows: console.log(`typ ...

Try logging in again if an error occurs

I've encountered some failing tests that we suspect are caused by network drops. To address this problem, I have modified my login method to retry after an error is detected. I would also like to have the number of retry attempts displayed in the cons ...

Learn how to hide the dropdown menu in Angular 8 by detecting clicks outside of the menu area

Is there a way to hide the custom dropdown menu whenever I click outside of it? After trying the code below, I noticed that it hides even if I click on an element inside the dropdown menu: <button class="btn btn-primary btn-sm mt-1" type="button" id= ...

Is there a way for me to set a variable in my component with a value from the Angular Material autocomplete feature?

I am currently in the process of constructing a form to generate a POST request to the API. I have opted to utilize Angular Material 4 and incorporate the Autocomplete component provided by Material Design. Below is the code snippet displaying my HTML Com ...

Incorporate an interface for handling potential null values using Typescript

Currently, I am utilizing Express, Typescript, and the MongoDB Node.js driver to develop my API. However, I am encountering an issue with the findOne method as it returns a promise that resolves with either an object containing the first document matching ...

Produce configuration files on the fly for Angular Component Testing using @Component declarations

Looking to test an Angular 11 component: @Component({ selector: 'app-foo-page', template: ` <app-header mode='operational' cool='true'></app-header> Some content ` }) export class FooPageComponent { } ...

How to access enums dynamically using key in TypeScript

export enum MyEnum{ Option1, Option2, Option3 } string selection = 'Option1'; MyEnum[selection] results in an error: The type string cannot be assigned to the type MyEnum On the other hand: MyEnum['Option1'] works as ...

Developing the headers for a service using React.js

As someone new to ReactJs, I'm curious about the various methods we can use to include Headers in our service Url before making a call. While I'm familiar with how GET/POST Calls are made in angular Js after including headers, I'd like to l ...

Encountering an issue with NgRx store where the property 'products' is not recognized on the type 'ActionCreatorProps<{ payload: Product[]; }>' while attempting to build a reducer

Currently, I am delving into the @ngRx/store package within my Angular 14 App. My primary goal is to establish a basic store capable of containing a simple object Array. Here is an excerpt from my action file: import { Product } from 'src/app/data-mod ...

Handle empty response from endpoint response

I'm facing an issue with a method that subscribes to an endpoint for a response. In some cases, the endpoint returns null and I need to handle this scenario. public list: Array<any>; this.GetList(this.getListRequest) .subscribe( (resp) =& ...

Using TypeScript to chain observables in a service and then subscribing to them in the component at the end

Working with Platform - Angualar 2 + TypeScript + angularFire2 Within my user.service.ts file, I have implemented the following code to initiate an initial request to a firebase endpoint in order to fetch some path information. Subsequently, I aim to util ...

verifying the date in a specific moment

Is there a way to validate the date accurately? to determine whether she cleared it or not. I've exhausted all my options. Despite reading through the documentation, I am unable to get it right. Here is what I attempted: if ('2023-03-03 ...

What is the reason that {a: never} is not the same as never?

Is there a reason the code {a: never} cannot be simplified to just never? I believe this change would resolve the issues mentioned below. type A = {tag: 'A', value: number} type B = {tag: 'B', value: boolean} type N = {tag: never, valu ...

The 'payload' property is not found within the 'Actions' type

I recently started using TypeScript and Visual Studio Code. I encountered the following issue: *[ts] Property 'payload' does not exist on type 'Actions'. This is my code: action.ts file: import { Action } from '@ngrx/store&apos ...

Guide on integrating a plain Service/Provider into nest.js

I recently created a basic TypeScript class in nest.js called JwtTokenService.js. // JwtTokenService.js import { Injectable, Optional } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { JwtPayload } from ' ...

Is it possible to generate a user profile using Firebase Cloud Functions and assign the user id as the document id?

I'm having trouble generating a user profile document in Firebase cloud functions using the user.uid as the doc id. Below is the script I am working with, but it keeps failing. I suspect there might be a syntax issue, so any suggestions would be great ...

Set up a SQS queue to receive notifications from an SNS topic located in another AWS account by using AWS CDK in TypeScript

Looking to establish a connection between an SQS queue and an SNS topic located in a different account using CDK (TypeScript). Presented below is the code snippet (contained within a stack) that I believe should facilitate this integration. However, I have ...