The process of implementing a statically typed Record of Functions on an argument in TypeScript

I am working with a set of function factories that all have the same return types, each returning an operation (in this case, a CRUD operation). The return type of these operations is a Promise for simplicity.

My goal is to convert an object of function constructors into an object of functions returned by these constructors, while maintaining static typing. In other words:

{f1: (k: K) => (...) => Promise<...>, f2: (k: K) => (...) => Promise<...>}

should become

{f1: (...) => Promise<...>, f2: (...) => Promise<...>}

when a key K is applied.

For example:

type K = 'a' | 'b';

const makeGet = (k: K): (id: string): Promise<DbRecord<K>> => {...}
const makeSet = (k: K): (id: string, Partial<DbRecord<K>>): Promise<void> => {...}

const fs = {get: makeGet, set: makeSet} as const;

export const makeCrud = (k: K) => mapValues(fs, (f) => f(k)); // Looking for a function like mapValues

Currently, I can only achieve a record-like return using lodash mapValues or fp-ts Record.map:

{f1: typeof f1(k) | typeof f2(k), f2: typeof f1(k) | typeof f2(k)}
. My aim is to obtain a more precise mapping:
{f1: typeof f1(k), f2: typeof f2(k)}

I believe fp-ts might have a solution for this, but I haven't found the right tool yet.

Is there a way to achieve this, or should I consider writing a custom mapValues function that respects the k=>v static relation?

Answer №1

To specify the type you need from your fs value, you can utilize a mapped type as shown below:

type MappedK<T extends Record<string, (k: K) => any>> = {
  [KK in keyof T]: ReturnType<T[KK]>
};

Playground

You can define your mapValues function (referred to as make) like this:

function make<T extends Record<string, (k: K) => any>>(vals: T, k: K): MappedK<T> {
  return Object.keys(vals).reduce((acc, objK) => ({
    ...acc,
    [objK]: vals[objK](k),
  }), {} as MappedK<T>);
}

export const makeCrud = (k: K) => make(fs, k);

This implementation should provide the correct types, although there might be some loopholes internally. The challenge arises when iterating over object properties due to TypeScript's constraints.

Refer to this GH comment for more insights

Regarding fp-ts

A noteworthy point about using fp-ts is that employing Record or ReadonlyRecord helpers is not feasible because these require uniform types. As a result, utilizing a mapped type becomes essential to maintain the connection between keys and their respective types.

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

Delivering static HTML routes in a React App using Typescript

I am working on a React app with functional components, and everything is working perfectly with its own CSS. Now, I have a separate static HTML file (FAQ) with its own CSS and design that I want to incorporate as a new route at /FAQ. I don't want th ...

Seamlessly linking TypeScript projects on both client and server side

My root project includes both the server and client side apps structured as follows: -- server -- node_modules -- index.ts -- package.json -- ... -- client -- node_modules -- index.ts -- package.json -- html/ -- css/ -- ... I'm s ...

React Native: Once a user has successfully logged in, I would like the app to automatically direct them to the "Home" screen

After a user signs in, I would like my app to navigate home. However, it seems this is not working because the roots have not been updated. You can view the App code here to get a better understanding of what I am trying to communicate. What is the most e ...

The argument being passed, which is of type 'unknown[]', cannot be assigned to a parameter of type 'SetStateAction<never[]>'

Looking to add typing to a React component, but encountered a TypeScript error while trying to assign setCharacters(arrayOfObj); export type CharacterItem = { filepath: string; group: string; id: string; isClicked: boolean; } export typ ...

Amaze the little ones by staggering children using framer-motion with a custom component as the child

Looking to implement a mobile menu with a cool fading effect on the navigation items, but something seems off. The NavLink items all appear simultaneously instead of staggered loading. Initially, considered using 'delay' instead of 'delayCh ...

Utilizing NgRx 8 Actions in Conjunction with NgRx 7 Reducers: An Implementation

In the development of my new Angular 8 project, I have incorporated the NgRx library. It was mentioned to me that actions are created using createAction in NgRx 8, but reducers are built using NgRx 7. Initially, I implemented my reducer with NgRx 8, but no ...

The user authentication service indicates that no user is currently logged in

Currently, I am working on implementing a route guard to distinguish between authenticated users and guests. In order to achieve this, I have created an auth-guard service along with an auth service. Although the user data is successfully stored in the loc ...

Transform a standard array of strings into a union string literal in TypeScript

I'm currently developing a module where users can specify a list of "allowable types" to be utilized in other functions. However, I'm encountering challenges implementing this feature effectively with TypeScript: function initializeModule<T ex ...

The TypeScript compiler is unable to locate the React Context namespace

import React, { useState, createContext, FC } from "react"; import { InitialInputValues, InputsInitiaState, } from "../components/AccountDetails/AccountDetails.type"; export const TestContext = createContext<InputsInitiaState> ...

TSLint Errors Update: The configuration provided cannot locate implementations for the following rules

After upgrading my tslint to version 4.0.2, I encountered numerous errors like the ones shown below: Could not find implementations for the following rules specified in the configuration: directive-selector-name component-selector-name directi ...

Receive a failure message from ngrx@effects and pass it to the component

I am encountering an issue with setting up server error validation for each input field in a form. The challenge lies in the fact that I am using ngrx@store, which presents a complication. @Effect({ dispatch: false }) error$ = this.actions$.pipe( o ...

Encountering TS7053 error while trying to access component variables using type indexing in Angular 13

When trying to access a variable with type indexing in Angular 13, I encountered a TS7053 error. However, in this Stackblitz example, the same code works without any errors. export class AppComponent { name = 'Angular ' + VERSION.major; Pro ...

Utilizing React TypeScript: Leveraging useRef for Linking purposes

Implementing useRef to Handle Link Clicks import {Link} from 'react-router-dom'; const myLinkRef = useRef<HTMLAnchorElement>(null); ... myLinkRef.current.click() ... <Link to={{pathname: '/terms'}} id='myLink' ref= ...

Merge generic nested objects A and B deeply, ensuring that in case of duplicate properties, A's will take precedence over B's

Two mysterious (generic) nested objects with a similar structure are in play: const A = { one: { two: { three: { func1: () => null, }, }, }, } const B = { one: { two: { three: { func2: () => null, ...

Despite passing the same dependency to other services, the dependencies in the constructor of an Angular2 Service are still undefined

To successfully integrate the "org-agents-service" into the "org-agents-component," I need to resolve some dependencies related to the required api-service. Other components and services in the hierarchy are also utilizing this api-service, which acts as a ...

Is there a way to retrieve the directory path of a folder that a user selects using a file input, without the need for them to actually upload the contents of the folder?

Is there a way to retrieve the selected folder path from the user without having them upload the files inside the folder? My goal is for the user to specify the location where they want to save the file that I will supply. I am looking to only display th ...

Testing the injection of ChangeDetectorRef using pipesWould you like to test the

I am working with a pipe that looks like this: @Pipe({name: 'myPipe', pure: false}) export class MyPipe implements PipeTransform { constructor(private _ref:ChangeDetectorRef) { _ref.markForCheck(); } public transform(input: ...

Obtain numeric sub-string from a string in typescript

Here is a sample string format: https://i.sstatic.net/SQ1ez.png https://i.sstatic.net/8Rg1C.png I need to extract the Revision number from the name. I attempted using Substring() but I require a universal solution that can retrieve the Revision number fr ...

Creating an HTTP method handler function in Next.js API routes with an unspecified number of generic parameters

Looking to create a wrapper function in NextJS for API routes that can handle multiple HTTP methods with different handlers. For example, check out the TS playground interface GetResponse { hello: string, } // empty object type PostResponse = Record&l ...

How can I transform this statement into a higher-order function that offers a resource instead of using an object for initialization and destruction?

Starting with this code snippet: convert utilizes svgInjector to start and terminate a resource. export async function convert( serializedSvg: string, svgSourceId: string, containerId: string ): Promise<string> { const svgInjector = new SvgI ...