Converting an array into an object by using a shared property in each element of the array as the key

I have an item that looks like this:

const obj = [
  { link: "/home", title: "Home1" },
  { link: "/about", title: "About2" },
  { link: "/contact", title: "Contact1" }
] as const

and I want to produce items in the following format:

const menu: MenuItems = {
  "/home": [{ link: "/home", title: "Home2" }] // can also include Home1, but not Contact1
}

Is it achievable to create a type for this? Here's my approach to clarify my intent:

type Obj = typeof obj;
type Item = Obj[number]

type MenuItems = {
  [K in Item["link"]]: Array<Item extends {link: K} ? Item : never>;
}

codesandbox: https://codesandbox.io/s/56564

Answer №1

Although you are on the right track, the issue lies in the fact that your T is a concrete type and not a generic type parameter. Therefore,

T extends { pointer: K } ? T : never
does not distribute over unions as expected. This means that T extends { pointer: K } will simply return false for each choice of K, resulting in never[] instead of the desired outcome.

To regain distributive conditional types, it's recommended to use a type alias similar to

type Something<X> = X extends { pointer: K } ? X : never
, and then substitute Something<T>. Fortunately, the built-in utility type Extract<T, U> already functions this way. So you can utilize the following:

type M = { [K in T["pointer"]]?: Array<Extract<T, { pointer: K }>> };

This will result in:

/* 
type M = {
    "/name"?: ({
        readonly pointer: "/name";
        readonly name: "N1";
    } | {
        readonly pointer: "/name";
        readonly name: "N2";
    })[];
    "/zip"?: {
        readonly pointer: "/zip";
        readonly name: "Z1";
    }[];
}
*/

This output aligns more closely with your expectations. Note that I made the properties optional since your constant m lacks some properties:

const m: M = {
    "/name": [{ pointer: "/name", name: "E2" }]
}; // should work

I hope this clears things up; best of luck!

Link to Playground with code

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

Access to the server has been restricted due to CORS policy blocking: No 'Access-Control-Allow-Origin'

I’m currently encountering an issue with displaying API content in Angular and I’m at a loss on how to troubleshoot it and move forward. At this moment, my main objective is to simply view the URL data on my interface. Does anyone have any insights or ...

The Context API's `useContext` hook appears to be malfunctioning, persistently

My situation is as follows: export const LocationContext = createContext(null); export const LocationProvider = LocationContext.Provider; export const useLocationContext = () => useContext(LocationContext); Using the Provider: export const Search = () ...

Explore Angular's ability to transform a nested observable object into a different object

My task involves mapping a field from a sub object in the response JSON to the parent object The response I receive looks like this: { "id": 1, "name": "file-1", "survey": { "identifier": 1, "displayedValue": survey-1 } } I am attempting ...

Issue with page break functionality during print preview

div.pagebreak { page-break-after: always; page-break-inside: avoid; } HTML <!-- Page separator --> <div class="pagebreak" style="float: none;"><hr class="hidden-print" /></div> <app-mud-chec ...

Ways to activate auto completion without using a string

Can anyone assist us with the tinymce editor? We have implemented an auto completion feature using a plugin from TinyMCE's documentation, but we are having trouble changing the triggering behavior. Currently, it only suggests options when "@" is typed ...

"Using Angular and TypeScript to dynamically show or hide tabs based on the selected language on a website

When switching the language on the website, I want to display or hide a specific tab. If the language is set to German, then show the tab; if any other language is selected, hide it. Here's my code: ngOnInit(): void { this.translate.onLangChange.s ...

Issue encountered in Loopback 4: Error with @repository dependency injection (TypeError: Undefined property 'findOne')

As I set up Bearer Token authentication for my Loopback 4 application, I referenced this implementation guide: https://github.com/strongloop/loopback-next/tree/master/packages/authentication. Within my src/providers/auth-strategy.provider.ts file, I encou ...

Angular dynamic array binding binds to multiple elements rather than just one

In my code, I am working with an array object structured as follows: let myArray=[ { "id":"100", "child1":[ {"id":"xx","Array":[]}, {"id":"yy","Array":[]}, {"id":"zz","Array":[]} ] }, { "id":"200", "child1":[ {"id":"xx","Array ...

Is there a way to retrieve the Incoming Message object in Angular using HttpClient?

From my Angular file, I am sending this request to the server. Is there a way to access it from my typescript file using a HttpClient object? IncomingMessage { ... // Headers details omitted for brevity url: '/teradata/databases/view/djfg', ...

Displaying Well-Formatted XML in Angular2 Using Typescript

After receiving this XML string from the server: <find-item-command xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" find-method="Criteria" item-class="com" only-id="false" xsi:schemaLocation=""> <criteria> <criterion> <descripto ...

Is there a way to position the label to the left side of the gauge?

Is there a way to position the zero number outside the gauge? I'm having trouble figuring out how to do it because the x & y options won't work since the plotLine's position keeps changing. The zero needs to move along with the plotLine and ...

Why is it considered an error when an index signature is missing in a type?

Consider the TypeScript code snippet below: type Primitive = undefined | null | boolean | number | string; // A POJO is simply meant to represent a basic object, without any complexities, where the content is unknown. interface POJO { [key: string]: ...

Tips for utilizing chodorowicz / ts-debounce effectively

Looking to utilize the debounce function provided by the ts-debounce package (available at here) in my typescript project. However, struggling to find a concrete example of its usage in typescript. Would greatly appreciate any help or guidance on this ma ...

What steps can I take to improve this code and prevent the error "Property 'patient' does not exist on type 'Request<ParamsDictionary>'" from occurring?

I'm having some issues with my code. I am attempting to use passport authenticate in order to save patient information that is specific to the token generated for each individual. router.get("/current", passport.authenticate("jwt", { session: false }) ...

When using the e.target.getAttribute() method in React, custom attributes may not be successfully retrieved

I am struggling with handling custom attributes in my changeHandler function. Unfortunately, React does not seem to acknowledge the custom "data-index" attribute. All other standard attributes (such as name, label, etc.) work fine. What could be the issu ...

What is the most efficient way to update data multiple times by mapping over an array of keys in a react hook?

My question might not be articulated correctly. I'm facing an issue with dynamically translating my webpage using Microsoft's Cognitive Services Translator. I created a react hook for the translator, which works well when I need to translate a si ...

I am experiencing an issue with my date filter where it does not display any results when I choose the same date for the start and end dates. Can anyone help me troub

Having an issue with my custom filter pipe in Angular. When I select the same dates in the start and end date, it doesn't display the result even though the record exists for that date. I've noticed that I have to enter a date 1 day before or ea ...

In the search for "action.payload" using Redux Toolkit

const userSlice = createSlice({ name: 'user', initialState, reducers: { // Setting the user setUser: (state, action: PayloadAction<string | null>) => { state.user.email = action.payload; }, setLoading: (state, ...

Creating a new endpoint within the Angular2 framework using typescript

I am brand new to Angular2 and I would like to streamline my API endpoints by creating a single class that can be injected into all of my services. What is the most optimal approach for achieving this in Angular2? Should I define an @Injectable class sim ...

Guide for releasing a typescript package compatible with both node and react applications

I have developed an authentication library in TypeScript which I have released on npm. My goal is to make this library compatible for use in both Node.js projects and React projects created with create-react-app. However, I am facing an issue where one of ...