How can you define a generic type alias in TypeScript for a function that is also generic?

I am facing a challenge with creating a generic alias for a typed generic function. Despite my efforts, I have not been successful in achieving this. Here is an example of what I'm encountering:

// foo.tsx
function foo<T>(arg: T): T {
  return arg
}

type FooT = typeof foo  // works, but alias loses its generic nature: <T>(arg: T) => T
type FooParametersT = Parameters<typeof foo>  // alright: [unknown]
type FooReturnT = ReturnType<typeof foo>  // no issues: unknown

type GFooT<T,> = typeof foo<T,>  // problematic
type GFooParametersT<T,> = Parameters<typeof foo<T,>>  // doesn't work
type GFooReturnT<T,> = ReturnType<typeof foo<T,>>  // unsuccessful

The main goal is to extract the function type from an external library and create an interface around it. For instance:

import { useState } from "react"

type UseStateFnT<T,> = typeof useState<T>
const wrapUseState: (toWrap: UseStateFnT<T,>) => UseStateFnT<T,> = …

I am wondering if there is a way to achieve this without manually reconstructing the intricate typed function signature on my own.

Answer №1

UPDATES FOR TS4.7+

Greetings once more! TypeScript 4.7 is set to introduce a new feature called instantiation expressions, detailed in this pull request by Microsoft. This update will allow you to specify type parameters for a generic function directly, without needing to call the function itself. Here's how it works:

type GFooT<T,> = typeof foo<T>  // (arg: T) => T
type GFooParametersT<T,> = Parameters<typeof foo<T>>  // [arg: T]
type GFooReturnT<T,> = ReturnType<typeof foo<T>>  // T

The syntax is almost identical to what was mentioned in the initial question, except that trailing commas are not permitted after type arguments (although they can still be used post-type parameter declarations). Exciting news indeed!

Link to Playground with code


PREVIOUS ANSWER FOR TS4.6-

In TypeScript, generics come in two forms: generic functions and generic types. It seems like you're aiming to transform one into the other, which isn't directly supported.


To clarify:

Generic types require type parameters to be specified before they can be utilized as a specific type. For example:

type GenType<T> = (x: T) => T[];
declare const oops: GenType; // error
declare const genT: GenType<string>; // okay
const strArr = genT("hello"); // string[];
const numArr = genT(123); // error!

In this scenario, GenType is a generic type. You must provide the type parameter to use it as the type of a value, transforming it from a generic to a specific type. The genT function takes a string and returns a string[]. It cannot process an input of number and output a number[].


On the other hand, generic functions have a specific type that can act as any substitution of its type parameters. The value remains generic even when the function is called, with the type parameter linked to the call signature:

type GenFunc = <T>(x: T) => T[];
declare const genF: GenFunc;
const strArr = genF("hello"); // strArr: string[];
const numArr = genF(123); // numArr: number[];

Here, GenFunc refers to a specific type representing a generic function, maintaining its generic nature during usage.

Generic functions, including generic constructor functions, could be seen as generic values, distinct from generic types.


While these two types of generics are interconnected, the TypeScript type system lacks the capability to articulate their relationship. In other languages, you might define one in terms of the other like:

type GenFunc = forall T, GenType<T>; // not possible in TS, error

or

type GenType<T> = instantiate GenFunc with T; // not feasible in TS, error

Such transformations in the type system from GenFunc to GenType are currently beyond TypeScript's scope. However, future enhancements such as higher kinded types might enable this functionality (microsoft/TypeScript#1213).


There exist complicated methods to coerce the compiler into deriving GenType from GenFunc. One approach involves leveraging generic class property initialization alongside higher order type inference for generic functions, first introduced in TypeScript 3.4. By creating artificial values within the compiler context, we can extract the desired type:

class GenTypeMaker<T> {
    getGenType!: <A extends any[], R>(cb: (...a: A) => R) => () => (...a: A) => R;
    genType = this.getGenType(null! as GenFunc)<T>()
}
type GenType2<T> = GenTypeMaker<T>['genType']
// type GenType2<T> = (x: T) => T[]

This methodology demonstrates that GenType2<T> mirrors the same type as GenType<T>, adjusting dynamically with changes to GenFunc. While technically feasible, I hesitate to endorse this workaround due to complexity and lack of scalability, especially if extending it across multiple object properties.


Playground link showcasing the 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

Use the rowTemplate in a Kendo grid without replacing the existing one

I am currently utilizing Angular 1.4 with TypeScript and Kendo UI (employing angular directives). My goal is to create a RowTemplate for each row that dynamically changes the color based on a specific property of the item. While I am aware of jQuery solu ...

Guide on creating a decorator for asynchronous functions that runs exclusively when using `Promise.resolve()`?

This decorator is specifically designed for analytics that triggers an event when a Promise is successfully resolved. class Foo { @LogEvent("success") async bar() { await someAction(); } } After researching online, it seems like I need to a ...

Bring in personalized tag to TypeScript

I am working on a TypeScript file to generate an HTML page. Within this code, I want to import the module "model-viewer" and incorporate it into my project. import * as fs from "fs"; import prettier from "prettier"; import React from "react"; import ReactD ...

Tips for effectively simulating the formik useFormikContext function while writing unit tests using jest

I've created a simple component (shown below) that aims to fetch data from the Formik FormContext using the useFormikContext hook. However, I'm facing some challenges when writing unit tests for this component. It requires me to mock the hook, w ...

Deleting an element from an object in TypeScript

Is there a way in TypeScript to exclude certain elements (e.g. 'id') from an object that contains them? ...

Error in Typescript: The property 'a' is not defined in the type 'A'

Here is an example of the different types I am working with: type Place = { address: string } type Location = { latLng: string } type User = { name: string } & (Place | Location) When attempting to parse the data using this structure, I enco ...

What is the best way to retrieve entire (selected) objects from a multiselect feature in Angular?

I'm facing an issue with extracting entire objects from a multiselect dropdown that I have included in my angular template. Although I am able to successfully retrieve IDs, I am struggling to fetch the complete object. Instead, in the console, it dis ...

Determine the route path during the ongoing navigation event in Angular 8 using NavigationStart

Looking for a way to retrieve the router path during a NavigationStart event in Angular 8 this.router.events .pipe(filter(event => event instanceof NavigationStart)) .subscribe((event: NavigationStart) => { // need help gett ...

Step-by-step guide on programmatically activating a radio button

I am working with a radio button and input field. I need the ability to programmatically toggle the radio button so that when this.iAreaOfCoverageForThresholdPasser.average-height is set to true, the radio button appears highlighted. Snippet of HTML: < ...

Mapping objects in Typescript to create a union of objects

I have been working on some TypeScript code and I seem to be having trouble getting it to work as expected. It would be greatly appreciated if someone could help me understand what I'm doing wrong or suggest a different approach. Let's assume I ...

Guide on how to add a generic return type to a function in typescript

Is there a way to annotate a function that returns a factory in TypeScript to ensure it contains correct type definitions? Consider the following code: class item<T> { constructor(a: T) { this.a = a; } a: T } function generate(c) { ret ...

How to pass a String Array to a String literal in JavaScript

I need to pass an array of string values to a string literal in the following way Code : var arr = ['1','2556','3','4','5']; ... ... var output = ` <scr`+`ipt> window.stringArray = [`+ arr +`] & ...

The compilation of TypeScript and ES Modules is not supported in Firebase Functions

Recently, I integrated Firebase Functions into my project with the default settings, except for changing the value "main": "src/index.ts" in the package.json file because the default path was incorrect. Here is the code that was working: // index.ts cons ...

Issue with Material UI v5: Uncaught TypeError - Unable to access properties of an undefined object (specifically 'create')

After setting up the ThemeSetting.tsx context, I encountered an issue where I could not utilize <Button><Button> and other components that rely on the theme from Material UI React.js in TypeScript: error TypeError: Cannot read properties of u ...

Error TS2322: The function expecting a type of 'FormEventHandler<HTMLFormElement>' cannot be assigned the type '(data: TicketFullDTO) => Promise<void>'

I am currently working on creating an edit form to modify data from a database based on its ID. Here is my approach: import React, {FormEvent, useEffect, useState} from "react"; import TextField from "@material-ui/core/TextField" ...

Retrieving information from a JSON object in Angular using a specific key

After receiving JSON data from the server, I currently have a variable public checkId: any = 54 How can I extract the data corresponding to ID = 54 from the provided JSON below? I am specifically looking to extract the values associated with KEY 54 " ...

Uncovering the Image Orientation in Angular: Is it Possible to Determine the Direction Post-view or Upon Retrieval from Database?

I am currently working on creating centered and cropped thumbnails for images retrieved from a database. I came across some helpful information on how to achieve this: The resource I found is written for JavaScript, but I am using Angular 7. I am facing d ...

TypeScript version 3.7 has implemented a new feature where it will now display errors for each individual invalid prop instead of grouping them together as it

Scenario using TypeScript 3.5.3 https://i.stack.imgur.com/wykd6.png link to interactive playground - TS 3.5.3 demo running successfully Example with TypeScript 3.7.2 https://i.stack.imgur.com/BPckB.png link to demo - TS 3.7.2 demo not functioning correctl ...

During my attempt to convert my Slice.js file to ts using the redux toolkit, I encountered some Type-errors

After creating a sample Redux toolkit with JavaScript files, I am now attempting to convert them to TypeScript. Some errors have been resolved, but I am facing issues with the following two errors: The error "Property 'name' does not exist on ty ...

The type entity i20.CdkScrollableModule cannot be resolved to symbol in the nx workspace

After extensively researching online, I still haven't found a solution to this particular issue. I've attempted various troubleshooting steps, including deleting node_modules and package-lock.json, updating dependencies, and running nx migrate. ...