Is there a way to retrieve the type of a generic class in JavaScript?

class Alpha {
  static construct<T extends typeof Alpha>(this: T): InstanceType<T> {
    const v = new Alpha();
    return v as InstanceType<T>;
  }
}

class Beta extends Alpha {}

const x = Alpha.construct(); // generates Alpha
const y = Beta.construct(); // yields Beta

Playground

I have now introduced generics to Alpha and Beta, resulting in this:

class Alpha<T = string> {
  static construct<T extends typeof Alpha>(this: T): InstanceType<T> {
    const v = new Alpha();
    return v as InstanceType<T>;
  }
}

class Beta<T = number> extends Alpha<T> {}

const x = Alpha.construct(); // generates Alpha<unknown>
const y = Beta.construct(); // yields Beta<unknown>

Playground

In this scenario, I anticipate receiving Alpha<string> and Beta<number>, but the default values for generics are not being applied while using InstanceType<T>.

Is there a way to include a generic and use a common method like .construct() above where both classes return instances with defaulted generics?

I also attempted T['prototype'] without success.

I was hoping to create a custom InstanceType, but it seems unattainable. Below is TypeScript's default definition for InstanceType, which may not access the generics.

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any

Answer №1

T pertains to a specific example of the category, and may vary for each instance. The method create within your class is marked as static, meaning it is not associated with any single instance of the class; thus, it does not inherit a generic T from the class.

The declaration <T = string> serves merely as a default setting. It does not constrain the potential values of

T</code in any manner. Consequently, when invoking a static method, your <code>T
essentially becomes unknown. Both Alpha and Beta can accommodate any value for
T</code, leaving you unaware of what <code>T
will be assigned to the instance generated through create() unless an explicit generic argument is provided.

Below shows the outcome when supplying an explicit generic parameter to create():

class Alpha<T = string> {
  // I've chosen a distinct name for this generic to indicate its independence from `T`
  static create<C = string>(): Alpha<C> {
    return new Alpha<C>();
  }
}

const a = Alpha.create<number>(); // yields Alpha<number>
const b = Alpha.create<[]>(); // yields Alpha<[]>
const c = Alpha.create(); // yields Alpha<string>

To extract the value of T from an Alpha, utilize this utility type:

type AlphaType<A> = A extends Alpha<infer T> ? T : never;

type A = AlphaType<typeof a> // displays number

Interactive Link

Answer №2

To create more constrained types, you can define T within Alpha and its subclasses:

class Alpha<T extends string = string> {
  static instantiate<T extends typeof Alpha>(this: T): InstanceType<T> {
    return new this() as InstanceType<T>;
  }
}

class Beta<T extends number = number> extends Alpha {}

const instanceA = Alpha.instantiate(); // results in Alpha<string>
const instanceB = Beta.instantiate(); // results in Beta<number>

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

The VueJS function is not defined

Looking for a way to fetch data from graphql in my vue project and store it in a variable. The function is asynchronous and the value of rawID needs to be awaited. However, there is a possibility that it could result in undefined, causing an error in the ...

"Troubleshooting: Unspecified getInitialProps in Nextjs when passing it to a layout component

Greetings, I am a newcomer to Next.js and facing an issue with passing dynamic properties to the header component. Despite using getInitialProps in Next.js successfully, I encounter the problem of receiving 'UNDEFINED' when these properties are p ...

What is the best way to bring in local modules within the browser environment using Playwright?

Let me illustrate what I am attempting to achieve: ../src/Foo/Bar.ts represents a local TypeScript file This particular file contains code that is designed to function within a browser environment (it utilizes WebSockets), and therefore needs to be execu ...

Certain Material-UI components appear to lack proper styling

I found a tutorial on how to incorporate material UI into my app at this link: https://mui.com/material-ui/getting-started However, I noticed that some components are not styled as expected and customizing the theme seems to have no effect... This is how ...

Can the WebSocket interface be substituted with WebSocket itself?

I'm currently in the process of setting up a WebSocket.Server using ws, Express 4, NodeJS, and TypeScript by following a guide. However, I've encountered an issue with the provided code not working as expected from the tutorial found at . In ord ...

Using a single Material Autocomplete input to handle two values within Angular

Looking to implement a search feature using Material's autocomplete that can filter by either user name or user ID. The current implementation is partially functional in this Stackblitz. When selecting a user name from the autocomplete options with a ...

What is the best way to utilize the fresh Sanitizer API in Typescript?

Everything seems to be working well on Codepen, even without using window. It's surprising because I'm used to having to use window.x if ( 'Sanitizer' in window ) { console.log( 'sani', 'Sanitizer' in window ); } ...

The ES6 import feature conceals the TypeScript definition file

I currently have two definition files, foo.d.ts and bar.d.ts. // foo.d.ts interface IBaseInterface { // included stuff } // bar.d.ts interface IDerivedInterface extends IBaseInterface { // more additional stuff } Initially, everything was funct ...

getStaticProps will not return any data

I'm experiencing an issue with my getStaticProps where only one of the two db queries is returning correct data while the other returns null. What could be causing this problem? const Dash = (props) => { const config = props.config; useEffect(() ...

What is the reason behind RematchDispatch returning a `never` type when a reducer and an effect share the same name?

Recently, I made the switch from TypeScript 4.1.2 to 4.3.2 with Rematch integration. Here are the Rematch packages in use: "@rematch/core": "2.0.1" "@rematch/select": "3.0.1" After the upgrade, a TypeScript error p ...

"Converting to Typescript resulted in the absence of a default export in the module

I converted the JavaScript code to TypeScript and encountered an issue: The module has no default export I tried importing using curly braces and exporting with module.exports, but neither solution worked. contactController.ts const contacts: String[ ...

I'm facing issues with the angular-stl-model-viewer in my current Angular project

I recently attempted to incorporate an stl-viewer into my Angular project using the npm package angular-stl-model-viewer and managed to install all necessary dependencies without any issues. However, I encountered a problem where the viewer is not displayi ...

Issue encountered: `property does not exist on type` despite its existence in reality

Encountering an issue in the build process on Vercel with product being passed into CartItem.tsx, despite being declared in types.tsx. The error message reads: Type error: Property 'imageUrl' does not exist on type 'Product'. 43 | ...

How to generate an interactive text-box using Angular 8 and connecting the input to the component during form submission

When I attempt to add a dynamic text box after clicking a button, the text box is successfully added. However, I am facing an issue where I am unable to retrieve all the values (values of dynamically added text boxes) when the form is submitted. It seems t ...

Attempting to display two separate d3 line graphs within a single Ionic2 page

I am facing an issue with including multiple similar graphs on a single page within an Ionic2 application. I am utilizing the d3-ng2-service to wrap the d3 types for Angular2. The problem arises when attempting to place two graphs in separate div elements ...

"Null value is no longer associated with the object property once it has

What causes the type of y to change to string only after the destruction of the object? const obj: { x: string; y: string | null } = {} as any const { x, y } = obj // y is string now ...

Angular 8 combined with Mmenu light JS

Looking for guidance on integrating the Mmenu light JS plugin into an Angular 8 project. Wondering where to incorporate the 'mmenu-light.js' code. Any insights or advice would be greatly appreciated. Thank you! ...

Error: Missing provider for MatBottomSheetRef

While experimenting in this StackBlitz, I encountered the following error message (even though the MatBottomSheetModule is imported): ERROR Error: StaticInjectorError(AppModule)[CountryCodeSelectComponent -> MatBottomSheetRef]: S ...

Angular searchbar issue: Unable to assign parameter of type 'unknown' to parameter of type 'string'

I've been working on an Angular app to fetch data from an API successfully. However, I'm currently facing a challenge while implementing a search function in my component.ts file. Here is the entire service code: import { Injectable } from &apos ...

Determining the data type of a property within an interface using TypeScript

Is there a way to extract the type from an interface based on its property name in order to use it in a Record? I am struggling with the syntax needed to retrieve the type by property name. My goal is to make this process more future-proof so that if the i ...