Determine the accurate data types while transforming an array into an object using a specific key

I have an array of elements, each with a unique category string property. I aim to transform this into a structure where the category serves as the key and the original element is the value.

To tackle this, I first verify that I possess a correctly typed tuple

type ElementType = { category: string; [key: string]: any }

const createList = <T extends ReadonlyArray<ElementType>>(items: T) => items;

const elementList = createList([
  { category: 'x', data: 'example' },
  { category: 'y', data: 20 },
] as const)

// elementList[0].data is accurately inferred as 'example'
// elementList[1].data is accurately inferred as 20

Next, I endeavor to convert it into an object type.

type ElementDictionary = {
  [index in typeof elementList[number]['category']]: typeof elementList[number];
}

const categorized: ElementDictionary = undefined as any;

The dilemma here is that the value ends up becoming a union type of all possible values instead of being specific to that particular key, resulting in

type ElementDictionary = {
  x: { data: string | number };
  y: { data: string | number };
}

instead of

type ElementDictionary = {
  x: { data: string };
  y: { data: number };
}

Any insights on how to achieve individual item types in ElementDictionary?

Note: this code snippet is not from my current project; I have simplified the main issue for clarity

Answer №1

The reason for receiving a union is because the type of the property is set as typeof objectList[number], which has no connection to type.

If you are using an outdated version of TS, you can utilize Extract to extract only the item type that matches the current key's type. Playground Link

In mapped types, you can also employ the as clause (available since TS 4.1, PR) to map over the union of item types in the array. By selecting the type in the as clause as the key, you can obtain the corresponding item type in the mapping type parameter:

type MyObject = { type: string; [key: string]: any }

const createObjectList = <T extends ReadonlyArray<MyObject>>(items: T) => items;

const objectList = createObjectList([
  { type: 'a', value: 'foo' },
  { type: 'b', value: 10 },
] as const)

type ObjectDictionary = {
  [TItem in typeof objectList[number] as item['type']]: Omit<TItem, 'type>'>;
}

const typed: ObjectDictionary = undefined as any;

Playground Link

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

Universal Parameter Typing in Functions

I'm grappling with a concept that seems obvious to me, yet is disallowed by Typescript when all strict flags are enabled (presumably for valid reasons). Let me illustrate: We all understand the following: export interface Basic { value: "foo&q ...

Category of ambiguous attributes

I am currently working on developing a class that will store values for rows and columns, with each row/column identified by a string. I have provided some code below as an example: class TMatrix<TYPE>{ [idRow: string]: { [idCol: string]: TYPE ...

Is it possible to import a class from a different project or module in TypeScript?

I am attempting to modify the build task in Typescript within this specific project: https://github.com/Microsoft/app-store-vsts-extension/blob/master/Tasks/app-store-promote/app-store-promote.ts I am looking to incorporate an import similar to the one be ...

Having trouble retrieving information from a controller action in .NET Core and Angular 2

My Angular 2 service: private checkEmailAvailabilityUrl = 'api/admin/checkemailavailability'; checkEmailAvailability(email: string): Observable<boolean> { let params = new URLSearchParams(); params.set('email', email); ...

Concealing a navigation tab with Angular4 in Typescript: A tutorial

I have successfully implemented 3 tabs in my Angular 4 project. At the moment, I am focusing on working with the first two tabs and planning to tackle the third tab in the near future. To keep things clean and organized, I am looking to use JavaScript/Typ ...

Minimizing assets in Angular 7 by running the command ng build --prod

After running ng build --prod, the JavaScript and CSS files located in /assets are not being minified. Is there a way to minify these files? I've checked the angular documentation but couldn't find any relevant information. ...

Creating a realistic typewriter effect by incorporating Code Block as the input

I am looking to add a special touch to my website by showcasing a code segment with the Typewriter effect. I want this code block not only displayed but also "typed" out when the page loads. Unfortunately, I have been unable to find a suitable solution s ...

How to Dynamically Enable or Disable a Button in Angular 2 Based on Text Field Values

I am facing a situation where my UI contains multiple text boxes and dropdown fields. My goal is to activate a button on the UI as soon as one of these fields has a value. I have set up a function that is triggered by the ngModel values assigned to these f ...

Angular is encountering a circular dependency while trying to access a property called 'lineno' that does not actually exist within the module exports

I am working on an Angular project and using the Vex template. My project utilizes Angular 9 and Node.js v15.2.0. Every time I run the project with the command ng serve -o, it displays a warning message. https://i.stack.imgur.com/8O9c1.png What could b ...

What is the method to merge min and max validation errors when using React Hook Form?

<input {...register("subject", { maxLength: 50, minLength: 2, required: true, })} disabled={isLoading} id="subject" autoComplete=&q ...

What could be causing the lack of updates to my component in this todo list?

Based on my understanding, invoking an action in MobX should trigger a rerender for the observer. However, when I call the handleSubmit method in my AddTask component, it doesn't cause the TaskList observer to rerender. Should I also wrap AddTask in a ...

What is the procedure for invoking various functions upon the completion and loading of an iframe?

Is there a way to call different methods using JavaScript instead of the onload method, which calls the same method on loading and after loaded? I need to execute different functions - any suggestions? ...

Using Typescript to pass the setState function as a parameter

Consider the scenario below: // external file export const specificFunction = setState => { setState({ value: "some new string" }) } // component's file import { specificFunction } from "pathToFile" interface TState { ...

The OR operator in TypeORM allows for more flexibility in querying multiple conditions

Is there any OR operator in TypeORM that I missed in the documentation or source code? I'm attempting to conduct a simple search using a repository. db.getRepository(MyModel).find({ name : "john", lastName: "doe" }) I am awar ...

Utilizing a method from a separate class in Ionic 2

Having trouble using the takePicture() function from camera.ts in my home.ts. I keep getting an error message saying "No provider for CameraPage!" Any assistance on how to resolve this issue would be greatly appreciated, as I am new to this language and ju ...

The name 'Landbot' cannot be located. Have you meant to type '_landbot' instead?

I'm currently in the process of integrating Landbot into my React.js application with TypeScript. I'm following this [doc] 1. However, I'm facing an issue where the code inside useEffect (new Landbot.Container) is causing an error. 'C ...

Utilizing the composition API to dynamically update the state of an array in Vue

I am working on implementing a state using the composition API in Vue 3 with the code in the file below: // useNotifications.ts const state = reactive<Array<Notification>>([]); export function useNotifications() { return { state, add ...

Service error: The function of "method" is not valid

In one of my Angular 2 applications, I have a class that contains numerous methods for managing authentication. One particular method is responsible for handling errors thrown by the angular/http module. For example, if a response returns a status code o ...

What is causing the duplication of leaves when using this DFS implementation?

I created an algorithm to compare if two trees have the same leaves. https://i.sstatic.net/lpO2C.png Both trees display matching leaf numbers in the exact order, resulting in a true outcome. Below is the code that I formulated: function leafSimilar(root ...

The Angular performance may be impacted by the constant recalculation of ngStyle when clicking on various input fields

I am facing a frustrating performance issue. Within my component, I have implemented ngStyle and I would rather not rewrite it. However, every time I interact with random input fields on the same page (even from another component), the ngStyle recalculate ...