Ways to universally establish values of a mapped type

Take a look at the code snippet below:

type Properties = {
  item0: { item0: string };
  item1: { item1: string };
  item2: { item2: string };
  item3: { item3: string };
  item4: { item4: string };
};

type Func<N extends keyof Properties> = ({}: Properties[N]) => number;
const Wrap = <N extends keyof Properties>(inner: Func<N>): Func<N> => {
  return (v: Properties[N]) => inner(v) + 2;
};
type FuncSet = { [Property in keyof Properties]: Func<Property> };
const WrapSet = (inner: FuncSet): FuncSet => {
  return {
    item0: Wrap(inner.item0),
    item1: Wrap(inner.item1),
    item2: Wrap(inner.item2),
    item3: Wrap(inner.item3),
    item4: Wrap(inner.item4),
  };
};

Is there a way to refactor WrapSet so that it automatically iterates over the properties in Properties without the need to specifically list them out? The goal is to find a solution within the TypeScript type system without resorting to JavaScript methods.

Answer №1

When dealing with TypeScript, there is no straightforward approach to achieve this task without resorting to methods like type assertions. The language lacks the necessary expressive power to abstract over mapped types in a broad sense, especially without higher kinded types as indicated in microsoft/TypeScript#1213.

A possible solution could be:

const WrapSet = (inner: FuncSet): FuncSet =>
  Object.fromEntries(Object.entries(inner).map(([k, v]) => [k, Wrap<any>(v)])) as
  unknown as FuncSet;

This approach involves using Object.entries() to break down the object into its attributes, then mapping them to form new attributes, and finally utilizing Object.fromEntries() to reconstruct the object. Despite working effectively at runtime, this method employs type assertions and the any type to avoid type-related issues.

Attempting to enhance type safety would encounter obstacles related to higher-order generics and their constraints. While you may desire to define WrapSet as a specialized version of the more general mapper mapValues():

function mapValues<T extends object>(obj: T, map: <K extends keyof T>(v: T[K]) => T[K]) {
  return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, map(v)])) as T
}

Convincing the compiler that Wrap possesses the correct type becomes exceedingly difficult:

const WS = (inner: FuncSet) => mapValues(inner, Wrap); // error!
// -------------------------------------------> ~~~~

The suitable type definition appears as follows:

const Wrap = <N extends keyof Properties>(inner: FuncSet[N]): FuncSet[N] => {
  return (v: Properties[N]) => inner(v) + 2; // error!
//~~~~~~
};

However, the compiler struggles to grasp the intricate higher-order relationship in this scenario. While some manipulation of generics might bring you closer, the efforts may not be worthwhile in the end. For now, utilizing the type assertion version seems prudent, until TypeScript incorporates higher-order generics, which may not happen at all.

Playground link to code

Answer №2

Are you interested in learning how to loop through all the properties of an object? One way to achieve this is by using the Object.keys(myObj) method, which will give you an array containing all the properties defined in myObj (excluding properties inherited from the object's prototype).

You can then access the value of each property using square bracket notation.

Here is an example of how you can accomplish this:

const Transform = (input: FuncTransform): FuncTransform => {
  return Object.keys(input).reduce((result, key) => {
    result[key] = Transformation(input[key]);
    return result;
  }, {});
};

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

Ways to toggle checkboxes to show or hide their child items and subitems

I'm working on creating a straightforward menu that allows users to click on a parent checkbox to expand or collapse its children. The challenge I'm facing is how to make the parent checkboxes expand while hiding the children items within them wh ...

Real-time data not instantly stored using socket.io

In my current issue, whenever I receive a message with the action stop, I encounter a problem specifically with the setTotalScore function. Initially, the totalScore is set to 0, and upon receiving the message, it should update to match the user.score. How ...

The NGRX + Resolver issue arises when the component loads before the action is fully dispatched, causing interaction problems

I'm currently facing an issue with fetching data from a route and loading it into my state before displaying my detail component. To tackle this, I've implemented a resolver. Although my get request seems to be functioning, it appears that the AP ...

Issue with my TypeScript modules TS2307: Module not found

For my latest project, I decided to use the aurelia-typescript-skeleton as the foundation. To enhance it, I created a new file called hello.ts in the src folder. export class Hello { sayHello(name:string) : string { return 'Hello ' + name; ...

Issue with React-Toastify not displaying on the screen

After updating from React-Toastify version 7.0.3 to 9.0.3, I encountered an issue where notifications are not rendering at all. Here are the steps I followed: yarn add [email protected] Modified Notification file import React from "react" ...

Having trouble retrieving JSON file in Next.js from Nest.js app on the local server

Having just started with Next.js and Nest.js, I'm struggling to identify the issue at hand. In my backend nest.js app, I have a JSON API running on http://localhost:3081/v1/transactions. When I attempt a GET request through postman, everything functi ...

TypeScript integrated Cypress code coverage plugin

I've been attempting to integrate the @cypress/code-coverage plugin with TypeScript in my project, but so far I haven't had any success. Here is a breakdown of the steps I've taken thus far: Followed all the instructions outlined in https:/ ...

What is the procedure to prevent Angular CLI from including a specific typings file in my project configuration?

I've integrated JointJs into my Angular CLI project, but I'm encountering typing errors during the build process: https://i.sstatic.net/3ihS3.png The error messages point to the file node_modules/jointjs/types/joinjs.d.ts, which is not the corr ...

Encountered a type error during project compilation: "Type '(e: ChangeEvent<{ value: string[] | unknown; }>) => void' error occurred."

I recently started working with react and I'm facing an issue with a CustomMultiSelect component in my project. When I try to call events in another component, I encounter the following error during the project build: ERROR: [BUILD] Failed to compile ...

get a duplicate of an object

Is this the proper method for creating a duplicate of an object? class ObjectWrapper { private _obj; /*** * Copy object passed as argument to this._obj */ constructor (_obj: Object) { this._obj = _obj; } /** Return copy of this._ ...

Discovering the array item by its ID using Angular 2 with Typescript

Hey everyone, I'm currently working with asp.net mvc 5 and running into an issue. When attempting to retrieve an object by its id, it keeps returning undefined. The strange thing is that the objects display fine when checking console.log(this.vtypes). ...

Encountering the error message "express.default is not a function" while attempting to start the node server within a container

Whenever I try to start my node server in a remote container, I keep encountering an error stating "express.default is not a function." Can anyone help me figure this out? Here's the content of my main.ts file: import * as express from 'express& ...

Unable to break down the property 'desks' of '(0 , _react.useContext)(...)' due to its undefined nature

Trying to mock DeskContext to include desks and checkIfUserPresent when calling useContext is causing an error to occur: Cannot destructure property 'desks' of '(0 , _react.useContext)(...)' as it is undefined TypeError: Cannot destruct ...

Solution for dealing with error 'Cannot find Property length on type {} in React using TypeScript

Any ideas on how to fix the error "Property 'length' does not exist on type '{}'"? Below is the code snippet causing the issue: //component const SearchResults = ({ results }: { results: {} }) => { let pageCount = results? ...

Encountering a type mismatch error in Typescript while working with Redux state in a store file

It appears that I have correctly identified all the types, but could there be a different type of Reducer missing? 'IinitialAssetsState' is not assignable to type 'Reducer' The complete error message: Type '(state: { assets: n ...

Is it more efficient to have deps.ts per workspace or shared among workspaces?

Currently, I am in the process of setting up my very first monorepo for a Deno-based application. In this monorepo, the workspaces will be referred to as "modules" that the API code can import from, with each module having its own test suite, among other t ...

Unable to locate module in node_modules directory when attempting to import an image into TS

In my .tsx file, I am encountering an issue with the following code snippet: import L from 'leaflet'; import icon from 'leaflet/dist/images/marker-icon.png'; import iconShadow from 'leaflet/dist/images/marker-shadow.png'; The ...

How to trigger a component programmatically in Angular 6

Whenever I hover over an <li> tag, I want to trigger a function that will execute a detailed component. findId(id:number){ console.log(id) } While this function is executing, it should send the id to the following component: export class ...

Having trouble with building an Ionic3 project, getting the error message: "Execution failed for task ':app:processDebugResources'. > Failed to execute aapt"

My attempt to develop an android app in ionic 3 hit a roadblock when running 'ionic cordova build android' resulted in the error: Execution failed for task ':app:processDebugResources'. > Failed to execute aapt I have integrated plug ...

Angular: Issue with subscribed variable visibility on screen

I am currently developing user management functionality. When a button is clicked, the goal is to save a new user and display an incoming password stored in the system. Below is a snippet of my code: onClick() { /*Code to populate the newUser variable from ...