Maintaining the generic types in mapped types in TypeScript

In my current project, I have a unique design where a class contains instance methods that act as handlers, each representing a specific operation. These handlers take a reference as input and assign the output to a second parameter. To simplify this process, a proxy object is created using a third-party library, allowing direct invocation of the handlers.

type InputRef<T> = {
  current: T,
};
type OutputRef<T> = {
  current?: T,
};

class Original {
  increment(input: InputRef<number>, output: OutputRef<number>) {
    const { current: inValue } = input;
    
    output.current = inValue + 1;
  }
}

type Mapper<Fn> = Fn extends (input: InputRef<infer U>, output: OutputRef<infer V>) => unknown ? (input: U) => V : never;
type MyProxyGeneratedByThirdPartyJs = { [FnName in keyof Original]: Mapper<Original[FnName]> };

declare const proxy: MyProxyGeneratedByThirdPartyJs;

const result = proxy.increment(3); // 4

However, there is a limitation with the mapper function. It fails to support generic types, such as:

class Original {
  toBox<T>(input: InputRef<T>, output: OutputRef<{ boxed: T }>) {
    const { current: inValue } = input;

    output.current = { boxed: inValue };
  }
}

Due to this limitation, the type of proxy only accepts unknown, resulting in the loss of generic information like T.

My goal is to redefine the type of proxy to:

{
  toBox<T>(input: T): { boxed: T },
}

Instead of the current definition which is:

{
  toBox(input: unknown): { boxed: unknown },
}

Is there a possible solution to achieve this desired outcome?

Answer №1

Currently, TypeScript does not support expressing higher kinded types for generic functions at the type level. This limitation is highlighted in microsoft/TypeScript#1213. The definition of Mapper<Fn> using conditional types infers input type U and output type V, but falls short of capturing any higher-order relationship between them when Fn is generic.

While there are some specific circumstances where transformation of generic function types to other generics can occur, it requires emitting JavaScript code as part of the process. This transformation is limited to a single function at a time, preventing mapping of an object of functions without losing the generics.

One possible workaround could involve defining a proxySingleFunction() that takes a function and returns a new function with specific types. However, this method also involves emitting JS code and may not solve all use cases.

// Sample proxySingleFunction implementation
function proxySingleFunction<U, V>(
    f: (input: InputRef<U>, output: OutputRef<V>) => any
): (input: U) => V {
    return function (input: U) {
        // Implementation details here...
    }
}

In situations where you only need to represent typings for transformed objects from third-party sources, using type assertions might be necessary. By pretending to create proxies and manipulating types with type assertions, you can potentially achieve something close to the desired outcome within the current limitations of TypeScript.

// Pretended usage of type assertions
declare const psf: <U, V>(
    f: (input: InputRef<U>, output: OutputRef<V>) => any
) => (input: U) => V;

// Creating a pretend proxy object
const myProxyType = (true as false) || {
    increment: psf(Original.prototype.increment),
    toBox: psf(Original.prototype.toBox)
}

// Getting the pretend type of the proxy object
type MyProxyGeneratedByThirdPartyJs = typeof myProxyType;

Although using type assertions in this manner may not provide significant benefits over manually writing out the types, it serves as a workaround to simulate transformations when dealing with existing transformed objects.

Playground link to 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

Is there a way to load and play different sounds on multiple audio players based on the length of an array?

I am attempting to load various sounds (.mp3 audio) on separate audio players that are displayed on a single HTML page. The number of players displayed on the screen is determined by the length of the array. In this specific example, I have 3 elements in t ...

There is an issue with Node/Express not accurately updating the data model

I recently went through a tutorial on creating a RESTful API with Node.js and MongoDB. While it worked well overall, I encountered a few issues. My Player model is as follows: var player = new mongoose.Schema({ name: String, email: String, score: String } ...

Having trouble understanding why getStaticProps function is not loading before the main exported function

When I use npm run dev to troubleshoot this issue, it utilizes getStaticProps to process various d3 properties before injecting them into the main output function during runtime. However, it seems that getStaticProps is not running as expected - a consol ...

Receiving data from multiple sockets in Node.js with Socket.io

I recently started working with Node.js to develop an online game that acts as a server-side application. This application serves the .html and .js files to the client while managing the game's logic. I'm utilizing Socket.io for communication bet ...

"Looking to navigate to the bottom or shift focus to the bottom in an Angular application? Here's

In the div, I have added an element and I am looking to automatically scroll to the bottom of the div every time a new element is added. While I know how to achieve this using jQuery, I am unsure of how to do it using AngularJS. The id of the div is testC ...

What Happens When You Click on a Link in Google News Using the Mobile Chrome Browser

When using the Chrome Browser on mobile to open a news link from Google News, a new tab is opened. To return to Google News, users can click on the white-highlighted "left arrow" below or find a small header with a "<" symbol above to navigate back. Ho ...

Having trouble retrieving information from the database using socket.io and node.js

As a beginner in node.js and socket.io, I am experimenting with my simple project to fetch data from a database. My index.html serves as a real-time chat example using socket.io with a basic ajax request. <!doctype html> <html> <head> ...

Guide on Validating Several Email Addresses in a React Form using Angular 4

I need to input 50 email addresses with the same domain name (gmail.com). Currently, I am using a Reactive form but the code I have implemented is not working as expected. https://stackblitz.com/edit/angular-wfwfow If anyone could assist me with this, I ...

When clicking on the dress in the masque, how do I change the attire so that it is instantly applied to the masque?

$("#tail").kendoFlatColorPicker({ preview: false, value: "#000", change: select }); $("#head").kendoFlatColorPicker({ preview: false, value: "#e15613", change: select }); I implemented the color ...

Preserve the existing value and then check it against the updated value of a variable within JavaScript

I utilized an API that supplies me with information in JSON format, retrieved the price of a specific currency, and presented it on a screen using JavaScript. I encapsulated this process within a function that dynamically updates the information at set int ...

Swap a jQuery class with another if the class for ul li is currently active

I am currently developing a form builder and I would like to customize the appearance, specifically changing the color of the text. I want the text to be white when the class is set to active, and black when the class is not active. Is there a way to achi ...

Trouble accessing setState within an axios call in ReactJs

I've encountered an issue while attempting to set the state of the variable isCorrectAnswer within an axios call. The error message Cannot read properties of undefined (reading 'setState') is showing up in the console log. What mistake am I ...

Having trouble compiling for IOS using a bare Expo app? You may encounter an error message that reads "Build input file cannot be found."

Encountering Error When Running react-native run-ios on Bare Expo App I am experiencing an issue while trying to run the 'react-native run-ios' command on my Bare expo app. The error message I am receiving is: "Build input file cannot be found: ...

What is the reason for `then` generating a new promise rather than simply returning the promise that was returned by `

I've been curious about why, in a situation where the onFulfilled handler of then() returns a promise p2, then() creates a new promise p3 instead of simply returning p2? For example: let p1 = new Promise(function(resolve, reject) { resolve(42); ...

Exploring the functionality of the instanceof operator in Javascript

I'm currently developing a Node.js application and the project structure is as follows: [Project Folder] | |---[plc] | |--- plc.js | |--- scheduler.js | |---[source] | |--- source.js | |---[test] |--- test.js Th ...

Using jQuery's setInterval to consistently refresh the value of a data attribute

I am struggling to dynamically update the data-left value of a div with the class name "tw_marquee_scroller" every 1 second. The intended behavior is for the value to increment by 10 each time, starting at 10 and increasing by 10 in subsequent seconds. H ...

Ways to create a variable in a function that can adapt without being tied to a variable that changes

Apologies for the vague title, but I am uncertain of the correct term for my issue. I am working on dynamically generating a table of clickable elements using for loops in JavaScript. Each element, when clicked, should execute the same function but with di ...

What is the reason the useEffect hook does not function properly with a state variable within context?

Check out my code here. I'm trying to display the content of the array testingData, but it's not showing up. If I remove the useEffect hook, it works fine. Can you help me understand why and how to fix it? ...

Bringing in the component's individual module

I encountered the error message in my Angular application - Can't bind to 'formGroup' since it isn't a known property of 'form' - and managed to resolve it by including the import import { AddEditModule } from './add.edit ...

Sharing parameters between pages in Angular IonicPassing parameters between pages within an Angular Ionic application

Is there a way to pass parameters from the signup page to the signupotp page successfully? I am facing an issue where the OTP on the signupotp page is not being recognized because the parameters (email and mobile) are not getting passed properly. In my bac ...