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

Retrieve ag grid from TypeScript file

I am currently utilizing ag-grid-angular in my Angular application to showcase data. I have a button that is located outside the ag grid, and when it is clicked, I need to retrieve all row data from the grid. I am aware that there is an API available for a ...

Performance issues arise in Angular when multiple DOM elements are added through dynamic components with binding, causing lagging

I have implemented a nested expand/collapse feature using dynamic components in Angular 5. Although the functionality works well, the browser crashes or the scroll stops working when loading multiple DOM elements (resulting in jerky scroll). This feature ...

Using TypeScript to ensure the correct typing for the return type of AsyncThunk when utilizing the 'unwrapResult' method from Redux Toolkit

Struggling to determine the appropriate return type for an AsyncThunkAction in order to utilize it with the unwrapResult method from Redux Toolkit (refer to: Redux Tookit: Unwrapping Result Actions): Here is how the Async thunk is declared in the Slice: e ...

What is the process for obtaining the complete URL using the getDownloadURL() function along with a token?

An error occurred due to an unresolved FirebaseStorageError: "storage/object-not-found". The message indicates that the object 'k91a73uzb99' does not exist in Firebase Storage. This type of error is categorized under FirebaseError with a code of ...

Failure to update values in local storage using the React useLocalStorage hook

I've developed two unique custom hooks named useLocalStorage and useAuth. function getDefaultValue<T>(key: string, initialValue: T | null): T | null { const storedValue: string | null = localStorage.getItem(key); if (storedValue) { retur ...

Transferring HTML variables to an Angular Component

I am currently trying to transfer the information inputted into a text-box field on my webpage to variables within the component file. These variables will then be utilized in the service file, which includes a function connected to the POST request I exec ...

Is it possible to measure the CPU utilization in a TypeScript application programmatically?

Is there a method to calculate CPU usage as a percentage and record it in a file every 20 milliseconds? I'm interested in exploring different approaches for accomplishing this task. Your insights would be greatly appreciated! I've come across so ...

Switch up the Angular base URL using ngx-translate

I successfully integrated ngx-translate into my Angular project. Now, I want to dynamically change the base href based on the language selected from the header menu. Currently, the URL appears as: "localhost:4200". However, upon launching the project, it ...

Transitioning a codebase from @angular-builders/custom-webpack to NX for project optimization

I need help migrating my Angular project from using "@angular-builders/custom-webpack" build targets to transitioning to an integrated NX monorepo. When I run the command npx nx@latest init --integrated, I receive the following warning: Unsupported build ...

Increasing response buffer size in Node.js fetch for version 2.x.x

Currently in the process of implementing an API request using nodejs-fetch and I've encountered an issue. The documentation states that the maximum buffer size for fetch is 16kB, but the response I need to retrieve is 53 kB. This causes the .fetch() f ...

The value from select2 dropdown does not get populated in my article in Angular

I am attempting to link the selected value in a dropdown menu to an article, with a property that matches the type of the dropdown's data source. However, despite logging my article object, the property intended to hold the selected dropdown value app ...

The separator falls short of spanning the entire width of the page

For some reason, I can't seem to make the divider extend to the full length of the page. <TableRow> <TableCell className={classes.tableCell} colSpan={6}> <Box display="grid" gridTemplateColumn ...

The mystery of Angular 2: Unveiling why ActivatedRoute.params always returns an empty object

I've been facing an issue with accessing the :id route parameter in a router guard. It seems to always return an empty Object{}. Initially, I was unsure of how to approach this problem, so I referred to this question for guidance. However, it didn&ap ...

Using Typescript to combine strings with the newline character

Currently, I am delving into Angular2 and facing the challenge of creating a new line for my dynamically generated string. For example: input: Hello how are you ? output: Hello how are you? Below is the code snippet: .html <div class="row"> ...

Using the original type's keys to index a mapped type

I am currently developing a function that is responsible for parsing a CSV file and returning an array of objects with specified types. Here is a snippet of the code: type KeyToTransformerLookup<T> = { [K in keyof T as T[K] extends string ? never : ...

retrieve document data from firestore using the service

Is there a way to get real-time data from a Firestore document using a service? According to Firebase's documentation, you can achieve this by following this link: https://firebase.google.com/docs/firestore/query-data/listen?hl=es#web-modular-api I ...

The expansion feature of PrimeNG Turbotable

I'm currently facing an issue with the Primeng Turbotable where I am unable to expand all rows by default. You can find a code example of my problem at this link. I have already tried implementing the solution provided in this example, but unfortuna ...

What is the solution to the error message "Unable to assign property of undefined"?

I am currently working on an angular countdown timer and encountering a TypeError when attempting to access a variable from another component. I am struggling to identify the root cause of this issue. Here is the video tutorial that I am using as a referen ...

Angular 2 module that is loaded lazily - service does not follow singleton pattern

After successfully implementing lazy loading modules into my application, I have ensured that the app.module.ts is properly configured. @NgModule({ declarations: [ AppComponent, HeaderComponent, HomeComponent ], imports: [ BrowserMod ...

Error code TS1005: Compiling Typescript DefinitelyTyped assets encountered an issue

I'm confident in my setup, but I can't seem to get tsc to compile. Here's the branch of my repository: https://github.com/inosion/sample-atom-typescript-package/tree/add_react Although I have the latest versions of typescript, I'm uns ...