Extracting type from a variable in TypeScript

class A {

  public static find(): Query{
      const f = () => new this();
      return new Query(f);
  }

}

class B extends A {
}

class C extends A {
}

class Query {

    private readonly entity: () => A;

    constructor(entity: () => A) {
        this.entity = entity
    }

    public someMethod():???? {
       return this.entity()
    }
}

This snippet resembles the concept of ActiveRecords. I am unsure about how to define the return type for Query.someMethod(). Setting it as A would restrict us from using child classes as types, leading to errors like in the following scenario:

const monkey: C = C.find().someMethod()

This would result in a type error. If you have insights on handling such cases in TypeScript, kindly provide suggestions with care and consideration.

Answer №1

If you wish for a Query to maintain which subclass of A it holds, then the class should be made generic in the type T of that subclass (restricted to A):

class Query<T extends A> {

    private readonly entity: () => T;

    constructor(entity: () => T) {
        this.entity = entity
    }

    public someMethod(): T {
        return this.entity()
    }
}

Additionally, you would want A.find() to be generically similar so that it returns something like Query<this> using the polymorphic this type, enabling B.find() to return Query<B> and C.find() to return Query<C>. Unfortunately, since find is static, TypeScript does not directly support this types in static members. Refer to microsoft/TypeScript#5863.

Fortunately, there's a workaround (as mentioned in that issue) where you make the method generic and utilize a this parameter instead:

class A {
    public static find<T extends A>(this: new () => T): Query<T> {
        const f = () => new this();
        return new Query(f);
    }
}

This means that when you invoke find(), TypeScript will only permit you to do so if the object on which you're invoking it is of type new () => T (a constructor) for a certain T constrained to A. Let's put it to the test:

class B extends A { ⋯ }
const qb = B.find();
//    ^? const qb: Query<B>
const b = qb.someMethod();
//    ^? const b: B

class C extends A { ⋯ }
const qc = C.find();
//    ^? const qc: Query<C>
const c = qc.someMethod();
//    ^? const c: C

Seems promising.

Link to code Playground

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

What sets apart using (@Inject(Http) http: Http) from not using it?

Following a recent query, I now have a new question. What sets apart these two approaches? Here is the original code I used: import {Http, HTTP_PROVIDERS} from 'angular2/http'; @Component({ viewProviders: [HTTP_PROVIDERS], ..// constructor(h ...

Content is not displayed by Angular components

Currently working on a web application using Angular and I have set up a navbar with links to different components. However, when I click on a link in the navbar, the URL changes but the content of the components does not display. Any assistance would be g ...

Transitioning from ng-repeat filter to Typescript

As I migrate my project from AngularJS to modern Angular 8, one of the steps is converting JavaScript code to TypeScript. During this process, I encountered a challenging issue with the `ng-repeat` filter feature. Initially, my HTML template looked like t ...

Using ngClass to dynamically compare a single number with an array of numbers in Angular

Take a look at this: [ngClass]="{className: singleNumber == arrayOfNumbers} Is there a way to compare 1 === [1,2,3,4] ? It seems to work if I use arrayOfNumbers[0] ...

Using TypeScript to Limit the Generic Type Parameter Passed into a Function

I am working with a generic interface that has specific constraints applied to its parameters. Here is an example of how it is used: interface Fetcher<Params,Resp> { fetch(params: Params): Resp; }; In my code, I have a function that uses this inte ...

Troubleshooting in React: Addressing the issue of missing property 'children' in type '{ }' while it is required in type 'Props'

I am working with two components, card and cardList. My goal is to include the cardList component in the App component to display a list of fetched data in cards. However, I am struggling with connecting these two components in order to correctly display ...

The 'posts' binding element is assumed to have a type of 'any' by default

Currently, I'm working on a code project that involves graphql, react, and typescript. In the middle of the process, I encountered an error message stating "Binding element 'posts' implicitly has an 'any' type." I am unsure about w ...

Tips for resolving package conflicts while integrating Wagmi View into a React/Typescript application

I am facing an issue while attempting to incorporate wagmi and viem packages into my project. Currently, my project utilizes the react-scripts package with the latest version being 5.0.1, and Typescript is operating on version 4.9.5. However, upon trying ...

TypeScript and Redux mapDispatchToProps are not in sync

Below is my React component written in TypeScript: import React from 'react'; import {connect, ConnectedProps} from 'react-redux'; import logo from './assets/logo.png'; // import { Counter } from './features/counter/Count ...

Step-by-step guide to initializing a project using React with Typescript and a functional server-side script

I am working on a project that involves a React Typescript app (created using Create React App). In this project, I need to have an executable script that can run alongside the React app. Both the app and the script are intended to only run on local machin ...

Using Firebase: retrieving getAdditionalUserInfo in onCreate of a Firebase Cloud function

Can anyone help me figure out how to retrieve extra data from a SAML login provider in the backend (firebase functions)? I can see the data on the client side but I'm struggling to access it in the backend. I've specified these dependencies for ...

What is the best way to handle .jsx files in my library bundling process with Rollup?

Currently, I am in the process of developing a component library using storybook and rollup. Here is the configuration file rollup.config.mjs /* eslint-disable import/no-extraneous-dependencies */ import peerDepsExternal from 'rollup-plugin-peer-deps- ...

Using TypeScript to assign values to object properties

In myInterfaces.ts, I have defined a class that I want to export: export class SettingsObj{ lang : string; size : number; } Now I need to reference this class in another file named myConfig.ts in order to type a property value for an object called CO ...

Navigating the intricacies of debugging sub-domains in Angular projects using Visual Studio Code (VS

Currently working on a massive project utilizing micro-services. The unique design for clients/tenants requires visiting their specific subdomain to select a particular tenant. For example, https://ClientA.localhost:4200 and https://ClientB.localhost:4200. ...

Angular - The argument provided is not compatible with the parameter

I encountered the following TypeScript errors in app.component.ts: Issue: Argument of type '(events: Event[]) => void' is not assignable to parameter of type '(value: Event[]) => void'. Description: Types of parameters 'e ...

Fixing the (Missing: any) error in a create-react-app project using TypeScript

One of the challenges I'm facing is when I call the BookTracker component in my root App.tsx file, specifically with the prop book={MY_MOCK}. type BookParamsTypes = { title: string; pubDate: number; //... rest }; import { BookParamsTypes } fro ...

What is the best way to generate a dummy ExecutionContext for testing the CanActivate function in unit testing?

In my authGuard service, I have a canActivate function with the following signature: export interface ExecutionContext extends ArgumentsHost { /** * Returns the *type* of the controller class which the current handler belongs to. */ get ...

Next.js: Specify HTTP response code

I am working on a Next.js v12 application that is written in TypeScript. Within this application, I have created a custom error page called _error.tsx to provide a user-friendly experience for various errors such as 410, 404, and more. The issue I am faci ...

Unraveling the mysteries of the spread operator in Javascript

Having trouble finding the right keyword to search for my issue, as I am confused about JavaScript or TypeScript spread operator behavior. I encountered this problem while working on a drag and drop function today, but let me provide a simple example. Imag ...

Google Cloud PubSub does not automatically resend unacknowledged messages

The answer chosen for this particular question contains some pertinent details I currently have a subscription set up with the following parameters: https://i.stack.imgur.com/Bn0d4.png along with the following code snippet: const subscription = this.pub ...