Mapping an array of Type T in Typescript using typings

Suppose we have a type T:

type T = {
  type: string,
}

and we create a function that takes an array of T and returns an object where the keys are the values of each T.type and the values are objects of type T.

const toMap = (...args: T[]) => args.reduce((res, t) => ({
  ...res,
  [t.type]: t
}), {});

For example:

const a = { type: 'hello' };
const b = { type: 'world' };
const c = { type: 'foo' };

const map = toMap(a, b, c);

We expect the following result:

{
  hello: { type: 'hello' },
  world: { type: 'world' },
  foo: { type: 'foo' },
}

map.hello // returns { type: 'hello' };

// When accessing an unknown property:
map.bar // `property bar doesn't exist on type { hello: { ... }, world: {...}, foo: {...} }`

How can we write typings for this function?

Answer №1

To make your code more versatile, you can start by generalizing the T type.

function toMap<T extends { type: string }>(...args: T[]): { [type: string]: T } {
  return args.reduce((res, t) => ({
    ...res,
   [t.type]: t
  }), {});
}

If you want to further refine the types, you need to specify generic types for variable arguments, like toMap<A>(arg1: A),

toMap<A, B>(arg1: A, arg2: B)
.

However, there are a couple of drawbacks:

1) You have to create these overloads for different numbers of arguments, which is common in Typescript (similar to the way it's done with Object.assign).

2) By default, Typescript types { type: "test" } as { type: string }, making it impossible to directly infer the keys as "test". To work around this, you must cast the string literal to a specific narrowed down string type like { type: "test" as "test" }.

// Generic overload for one argument
function toMap<A>(arg: A): { [K1 in O<A>]: A };

// Generic overload for two arguments:
function toMap<A, B>(arg: A, arg2: B): { [K in O<A>]: A } & { [K in O<B>]: B };

// Add more overloads for additional arguments

// Implementation for various arguments
function toMap<T extends { type: string }>(...args: T[]): { [type: string]: T } {
   return args.reduce((res, t) => ({
     ...res,
    [t.type]: t
  }), {});
}

// Infer the type of "type" from an object
type O<V> = V extends { type: infer K } ? K extends string ? K : never : never;

// Narrow down a.type to be "test"
const a = { type: "test" as "test" }
const b = { type: "test2" as "test2", v: 1 };

const test = toMap(a);
const test2 = toMap(a, b);

console.log(
 test2.test2.v, // Works!
 test2.whatever, // Doesn't work!
 test2.test2.k // Doesn't work!
);

Give it a try!

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

Converting an array of objects into a unified object and restructuring data types in TypeScript

A few days back, I posted a question regarding the transformation of an array of objects into a single object while preserving their types. Unfortunately, the simplified scenario I provided did not resolve my issue. In my situation, there are two classes: ...

What is the best approach for sending mapbox coordinates to a higher-level class in a react application?

As a beginner in learning React, I'm currently working on setting a map marker with a click event on a Mapbox GL map. The challenge I'm facing is passing the lngLat object back up to the parent component. Can someone guide me on how to achieve th ...

`Some Items Missing from Responsive Navigation Menu`

Hey there! I'm currently diving into the world of responsive design and I'm attempting to create a navigation bar that transforms into a menu when viewed on a mobile device or phone. Everything seems to be working fine, except that not all the na ...

Adding a clickable button to execute code within a LeafletJS marker!

I'm currently experimenting with adding a button inside a pointer that will log a message to the console. This is simply a test to see if I can execute a method on the marker, but so far I haven't been able to display any text. const marker = L.m ...

Recalling the use of jquery toggle throughout the website?

I have successfully implemented a button to hide and unhide a lengthy menu with a click, but I am struggling to make the menu state persistent across multiple pages. I would like the last toggle action by the visitor to be remembered. Any suggestions on ho ...

Leverage the new Animation support in RC 5 to animate each item in an *ngFor list sequentially in Angular 2

I have a unique component that retrieves a list of items from the server and then displays that list using *ngFor in the template. My goal is to add animation to the list display, with each item animating in one after the other. I am experimenting with t ...

When utilizing Vue components, the issue of "setattr" arises when trying to assign

Having recently started using Vue.js, I encountered an issue while trying to implement components. Interestingly, the code worked perfectly fine before implementing components. I'm facing an error that is proving to be quite challenging to understand ...

How come the array's length is not appearing on the browser screen?

Code: initialize: function() { this.todos = [ {id: 100, text: 'Rich'}, {id: 200, text: 'Dave'} ]; }, activeTodos: function() { this.todos = this.todos.length(function() { return this.todos; }); ...

The function to refresh content is causing errors in the code

Creating a comment and replies form where the replies display underneath each comment. I've encountered an issue where I can post replies normally, but when attempting to delete a reply, I must refresh the page. After refreshing, I'm only able to ...

Transferring streaming data from Node.js to an ElasticSearch database

Currently, my Node.js script is extracting data from a large USPTO Patent XML file (approximately 100mb) to create a patentGrant object. This object includes details such as publication number, country, date, and type of patent. I am working on storing a ...

Add data to a DataTable without the need to manually refresh the page

I am looking to dynamically append a DataTable on my webpage. The goal is to have a form that users can fill out multiple times and each time they submit their inputs, the results will be added to the DataTable for display. <table id="table" class="di ...

The <g> tag fails to properly render within the <svg> element in Firefox

When running an Angular 6 application with ES6-style D3js modules, there are some issues on Firefox (Chromium, Chrome, Safari, and IE Edge work fine). Below is a sample of pseudo code (full production code is available below): <svg width="500" height=" ...

Drop down calendar in Javascript

I am in the process of developing a virtual JavaScript calendar dropdown feature. I aim to have the correct number of days displayed upon selecting a month, but currently, no days appear when I make a selection. Can someone please assist me with this iss ...

What's the best way to ensure that the theme state remains persistent when navigating, refreshing, or revisiting a page in the browser?

Is there a way to ensure that my light/dark theme settings remain persistent when users reload the page, navigate to a new page, or use the browser's back button? The current behavior is unreliable and changes unexpectedly. This is the index.js file ...

The data shown in the C3.js tooltips

I have successfully created a chart like this on jsfiddle: http://jsfiddle.net/q8h39/111/ The chart includes customized tooltip contents for each plot. I attempted to add extra data to the tooltip using the "indexOf()" method, but unfortunately, it did no ...

Odd Behavior when altering button attribute using Jquery

After disabling a button with a click handler, I have noticed an interesting behavior where the button does not submit afterwards. The issue seems to vary between different browsers, with Firefox still allowing form submission after the button is disabled, ...

Performing an AJAX request to validate a username when it loses

Hey everyone, I'm currently working on a script that checks the availability of a username in a MySQL database using an onblur event with AJAX. Here's the AJAX script: document.getElementById("r_username").onblur = function(){ var http ...

Functions perfectly on Chrome, however, encountering issues on Internet Explorer

Why does the Navigation Bar work in Chrome but not in Internet Explorer? Any insights on this issue? The code functions properly in both Internet Explorer and Chrome when tested locally, but fails to load when inserted into my online website editor. Here ...

Executing npm / http-server script

I'm currently working on a shell script that will compile an Angular app using the "ng build" command and then launch a web server to host the app from the dist folder. The web server will be started with the "http-server" command, which needs to be i ...

webpack is encountering difficulty resolving the node_modules material-icons directory

Encountering an Error ERROR in ./node_modules/material-icons/iconfont/material-icons.css (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[2]!./node_mod ...