Retrieving the return type of generic functions stored in a map

In this unique scenario, I have a dictionary with polymorphic functions that accept the same argument but return different results:

const dict = {
  one: {
    foo<A>(a: A) {
      return [a] as const
    }
  },
  two: {
    foo<A>(a: A) {
      return [a, a] as const
    }
  },
  three: {
    foo<A>(a: A) {
      return [a, a, a] as const
    }
  }
}

type Map = typeof map

The goal is to extract the function's return type from the given dictionary using a specific helper method:

const wrapper = <N extends keyof Map, A>() => {
  const fn = (() => {}) as unknown as Map[N]['foo']
  return null as unknown as ReturnType<typeof fn<A>>
}

An example of trying to obtain the result (such as what the function named two would return if passed 7 as the first argument):

// returns: "readonly [7] | readonly [7, 7] | readonly [7, 7, 7]"
// expected: "readonly [7, 7]"
type Result = typeof wrapper<'two', 7>

The issue arises as the generic N argument in the wrapper function is treated as a union even when only one value is provided as an argument.

The main challenge here is obtaining the accurate result without direct access to the dict object since it's part of a library implementation.

Playground Link for reference

Answer №1

As mentioned previously, TypeScript is currently unable to handle the type transformation you require. There is a need for features like "generic generics", "higher order generics", "type constructor parameters", or "higher kinded types" as discussed in microsoft/TypeScript#1213. While TypeScript allows specific type functions, writing a generic one is not possible.

To illustrate, consider an imaginary type-like entity called TypeFunc, where if F extends TypeFunc, then F becomes a generic type that accepts a type parameter such as F<T>. This concept would allow us to define:

// Hypothetical TypeScript syntax:
type Foo<F extends TypeFunc> = {x: F<string>}
type G = Foo<Array> // type G = {x: Array<string>}
type H = Foo<Promise> // type H = {x: Promise<string>}

In this scenario, while Array and Promise are specific type functions, F remains generic, enabling delayed application of type parameters to Array and Promise.

Potentially, we could define your wrapper() function like so:

// Theoretical TypeScript code:
type Input<T extends Record<keyof T, TypeFunc>> = 
  { [K in keyof T]: { foo: <A>(...args: any) => T[K]<A> }};
type Output<T extends Record<keyof T, TypeFunc>, K extends keyof T, A> = 
  T[K]<A>;

declare const f: <T extends Record<keyof T, TypeFunc>>(
  t: Input<T>) => <K extends keyof T, A>() => Output<T, K, A>;
const wrapper = f(dict);

However, due to the absence of higher kinded types in TypeScript, implementing this is not feasible.


Your utilization of instantiation expressions is intriguing but falls short of achieving the desired outcome. The current limitation in TypeScript leads to the formation of union types when calling indexed access types within the implementation of wrapper(), preventing full realization of the intended functionality described in microsoft/TypeScript#47240.

Despite the possibilities offered by instantiation expressions, they do not deliver the expected higher kinded types capabilities.


Exploring alternative options, a manual approach involving instantiation expressions for each key of dict may be used to construct a specific type function aligned with your requirements. By validating that dict aligns with this constructed type function, any divergence from the expected behavior would trigger warnings, as illustrated below:

type DictMap<A> = {
  one: ReturnType<typeof dict.one.foo<A>>,
  two: ReturnType<typeof dict.two.foo<A>>,
  three: ReturnType<typeof dict.three.foo<A>>,
};

const wrapper = <K extends keyof Dict, A>() => {    
  const dictSentinel: { [P in keyof Dict]: { foo(a: A): DictMap<A>[P] } } = dict;

  return null! as DictMap<A>[K];
}

The usage of DictMap<A>[K] accurately depicts the output of dict[k]<A> for keys of type

K</code, facilitating consistent behavior and immediate error detection if <code>dict
undergoes modifications.

While not elegant, this approach represents a practical workaround until the introduction of higher kinded types in TypeScript.

Link to Playground for full 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

Provider not found: ConnectionBackend – NullInjectorError

I encountered the following error while attempting to load the webpage. Despite trying various suggestions from other sources, I have been unable to find a solution. Below the error stack lies my code. core.js:7187 ERROR Error: Uncaught (in promise): Null ...

Outputting a JS file per component using Rollup and Typescript

Currently, I am in the process of developing a component library using React, TypeScript, and Rollup. Although bundling all components into a single output file index.js is functioning smoothly, I am facing an issue where individual components do not have ...

Angular: Effective communication between components through routing and Observable binding ultimately results in the advancement of ngtsc(233

I designed an Angular Component named "crear-pedido" that exhibits a catalog of items (using row of products) and my aim is for the user to have the ability to click on the values in the ID column and navigate the application to a subordinate component kno ...

When working with TypeScript in Node, the module ""http"" does not have a default export available

const httpModule = require('http'); httpModule.createServer((req, res) => { res.end('Hello World'); }).listen(3000, () => console.log('Server is running on port 3000')); I've installed @types/node but ...

AWS Amplify is failing to maintain the user session post a successful login

Currently, I am developing an aws-serverless application using React. My main issue lies in the authentication process with aws-amplify. The authentication works smoothly, but the session is not being preserved. During the signing-in stage: await Auth.s ...

Order of Execution

I am facing an issue with the order of execution while trying to retrieve values from my WebApi for input validation. It appears that the asynchronous nature of the get operation is causing this discrepancy in execution order. I believe the asynchronous b ...

The function `find()` will not provide any data, it will only output `undefined`

I am trying to extract the `s_id` field from this JSON data: { "StatusCode": 0, "StatusMessage": "OK", "StatusDescription": [ { "s_id": "11E8C70C8A5D78888E6EFA163EBBBC1D", "s_serial": " ...

Typescript-powered React component for controlling flow in applications

Utilizing a Control flow component in React allows for rendering based on conditions: The component will display its children if the condition evaluates to true, If the condition is false, it will render null or a specified fallback element. Description ...

The Server Components render encountered a glitch

Screenshot of the errorI am encountering a strange error only in the production environment. The lack of additional information leads me to believe it may be due to security measures put in place for production. Unfortunately, I have been unable to repli ...

The type of Object.values() is not determined by a union or Records

When utilizing TypeScript, the Object.values() function encounters issues in deducing the accurate type from a union of Records: type A = Record<string, number>; type B = Record<string, boolean>; function func(value: A | B) { const propert ...

What is the proper error type to use in the useRouteError() function in react-router-dom?

In my React project, I am utilizing the useRouteError() hook provided by react-router-dom to handle any errors that may arise during routing. However, I'm uncertain about the correct type for the error object returned by this hook. Currently, I have ...

How come code suggestion works flawlessly on Stackblitz, yet fails to function in Intellij?

Check out this GitHub repository for a simple way to reproduce the issue: https://github.com/tmtron/mathjs-typescript-types Successful Code Completion in Stackblitz Everything works perfectly when you open the project in Stackblitz: https://i.stack.imgur ...

Error: The @use directive must come before any other rules in Angular

Error message: Issue: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): Error Details: HookWebpackError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js) ...

Having difficulty updating the value of a FieldArray with setFieldValue in Formik

Thank you for taking the time to read this. I am currently working on creating a form using Formik that involves nesting a FieldArray within another FieldArray. Oddly enough, setFieldValue seems to be functioning correctly as I can log the correct values ...

Why do we often encounter a "SyntaxError: Unexpected token ;" error when a seemingly normal semicolon is present in distribution files?

Currently, I am in the process of developing a newsletter subscription API using node.js and typescript. This project involves my first experience with typeorm and PostgreSQL. Following several tutorials, I configured typeorm and created the entity types a ...

Rearrange an element in an array from last to first position using typescript

I am working with an array that looks like this var column = ["generic complaint", "epidemic complaint", "epidemic1 complaint", "epidemic2 complaint", "bal vivah", "name"] My goal is to move the last element of the array to the first position, resultin ...

The HTML canvas drawImage method overlays images when the source is modified

Trying to implement a scroll animation on my website, I came across a guide for creating an "Apple-like animation" using image sequences. Even though I'm new to Angular, I attempted to adapt the code to work with Angular. However, instead of animatin ...

Use JavaScript's Array.filter method to efficiently filter out duplicates without causing any UI slowdown

In a unique case I'm dealing with, certain validation logic needs to occur in the UI for specific business reasons[...]. The array could potentially contain anywhere from several tens to hundreds of thousands of items (1-400K). This frontend operation ...

Issues with TypeScript: Difficulty locating names in HTML templates

I recently upgraded my Angular 7 code to Angular 9 by following the steps outlined in the Angular Upgrade guide. However, upon completion of the migration process, I started encountering numerous "Cannot find name" errors within the HTML templates of my co ...

What is the best way to perform type checking for a basic generic function without resorting to using a cumbersome cast

Struggling with TypeScript and trying to understand a specific issue for the past few days. Here is a simplified version: type StrKeyStrVal = { [key: string]: string }; function setKeyVal<T extends StrKeyStrVal>(obj: T, key: keyof T, value: str ...