Explain the concept of a static async create method having identical parameters as the constructor

I have a lot of existing classes that require refactoring to utilize an async constructor.

Here's an example:

class ClassA {
    constructor(a: number, b: string, c: string) {
        //...
    }
    //...
}

I've included an async create method:

class ClassA {

    // added async 
    static async create(...args: ConstructorParameters<typeof ClassA>){
        await loadSameResources()
        return new this(...args)
    }

    constructor(a: number, b: string, c: string) {
        //...
    }
}

It functions properly, users simply need to replace new ClassA(...) with (await ClassA.create(...)), which is straightforward and minimizes necessary changes.

My issue lies in how to avoid specifying 'ClassA' within the create function, similar to the example below so I can easily replicate this function in every class (without altering class inheritance).

    // added async 
    static async create(...args: ConstructorParameters<typeof this >){ // error here
        await loadSameResources()
        return new this(...args)
    }

Answer №1

One approach is to have the constructor invoke a hidden initializer function, which simplifies parameter extraction.

class MyClass {

    // include async keyword
    static async initialize(...args: Parameters<typeof this.prototype.setup>){
        await loadResources(() => {})
        return new this(...args)
    }

    constructor(...args: Parameters<typeof this.setup>) {
        this.setup(...args)
    }

    private setup(x: number, y: string, z: string) {}
}

By having all constructors call the private setup method, it becomes straightforward for them and the initialize function to deduce their respective parameter types from setup.

This implementation allows you to easily transfer both functions to multiple classes without modifications needed.

If there's no need to expose the constructor, consider making it private as suggested by other users.

Answer №2

When it comes to altering the target semantics, there's a simple solution that involves creating a generic function to construct your instances:

import { Constructor } from 'type-fest'

async function create<TInstance>(Class: Constructor<TInstance>, ...args: ConstructorParameters<typeof Class>) {
    await loadSameResources()
    return new Class(...args)
}

const a = await create(ClassA, 0, 'b', 'c')
//    ^? const a: ClassA

Playground Link

Benefits:

  • The create function is centralized in one place for easier maintenance
  • No modifications needed to your current classes

Alternatively, if you want to maintain your target semantics ((await ClassA.create(...))) while using a slightly more complicated method, you can utilize a Class Mixin:

function WithCreateMixin<TInstance extends object>(BaseClass: Constructor<TInstance>) {
    return class BaseClassWithCreate extends BaseClass {
        static async create(...args: ConstructorParameters<typeof BaseClass>) {
            await loadSameResources()
            return new BaseClass(...args)
        }
    }
}

class ClassA extends WithCreateMixin(class ClassA0 {
    constructor(a: number, b: string, c: string) {
        //...
    }
    //...
}) { }

const a = await ClassA.create(0, 'b', 'c')
//    ^? const a: ClassA0

Playground Link

The create static function continues to be centrally defined, but now requires wrapping existing classes (with

class ClassX extends WithCreateMixin(/* original class */) { }
).

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

Concise way to add or insert an element into an array, at a specific property of an object

I am working with an object of the ObjectOfArrays type: type ObjectOfArrays = { [id: string]: Array<string> }; Throughout my code, I need to add strings to the correct array based on the id. Currently, I am using the following approach: if (id i ...

Exploring methods to access specific values from an array containing multiple values using Lodash in Angular 4

Hey, I have an array that looks like this: [ 0: "Migration, MD" 1: "Lution, MD" 2: "Mover, MD" 3: "Dee" 4: "Prov10A" ] I would like to extract the values that contain the word "MD" in them. In other words, I want a result like this: [ 0: "Migratio ...

Utilizing shared components across a Next.js application within a monorepo

Utilizing a monorepo to share types, DTOs, and other isomorphic app components from backend services (Nest.js) within the same mono repo has presented some challenges for me. In my setup, both the next.js app and nest.js app (which itself is a nest.js mono ...

Activate function on Ctrl + K in a React TypeScript project

I am currently developing a React TypeScript application using version v18.2.0. My goal is to trigger a function when the user simultaneously presses Ctrl + K. Here is the code snippet within my component: const keyDownHandler = (event: KeyboardEvent) =& ...

Error in Visual Studio 2019 - TypeScript version discrepancy

I am completely puzzled by the mismatch in Typescript versions when using Visual Studio 2019 with my .NET Core 2.2.x project. Every time I make changes to a .ts file, I receive the following warning (found in the error list): Your project is built using ...

Misunderstanding the concept of always being right

Here is a code snippet that raises an error in TypeScript: class Status { constructor(public content: string){} } class Visitor { private status: Status | undefined = undefined; visit(tree: Tree) { if (tree.value > 7) { this.status = new ...

Unable to assign user roles in next-auth due to the absence of matching modifiers for user

I am currently working on implementing user roles in next-auth. Within my database, I have defined a prisma enum UserRole with the values 'ADMIN' and 'USER'. In my auth.ts file, I included the role property in the session object and enc ...

Using *ngFor in TypeScript code

Hello there, I have a question about performing the logic of *ngFor in typescript. Is it possible to loop through data from my JSON API in the typescript file similar to how *ngFor works in HTML? Any guidance on this matter would be greatly appreciated. ...

Adding attributes to parent DOM elements of a component in Angular2: A Step-by-Step Guide

I'm working with the following code: ... <div class="container"> <div class="fancy"> <fancybutton></fancybutton> </div> <button (click)="addAttribute()">Remove</button> <button (click)="remAttr ...

Enhance your coding experience with TypeScript's autocomplete in Visual Studio Code

After migrating a project from JavaScript to TypeScript, I am not seeing autocomplete suggestions or type hints when hovering over variables in Visual Studio Code editor (Version 1.7.2). Even the basic example provided below does not display any auto-com ...

Module '@types/mongodb' could not be located

Currently, I am working on a Node.js application using Typescript with a MongoDb database. Unfortunately, I encountered an issue today related to importing the type definitions of MongoDb. When I try to import the Db type like this: import { Db } from "@ ...

Generating an iFrame in Angular with real-time data from Observable sources

I am looking to integrate multiple YouTube videos into my Angular application using iframes. The video URLs are stored in a database, and I need to fetch the 3 most recent ones on each visit. To achieve this, the "youtube" component makes a request to a ...

Increase the timestamp in Typescript by one hour

Is there a way to extend a timestamp by 1 hour? For instance, 1574620200000 (Including Microseconds) This is my date in timestamp format. How can I add a value to the timestamp to increase the time by an additional hour? ...

Tips for utilizing the "this" keyword in TypeScript

As a newcomer to TypeScript, I am seeking assistance on how to access the login service within the authenticate function. Despite using the 'this' keyword to retrieve the login service, it seems ineffective. Additionally, I find myself puzzled by ...

Find the object in the array that has a name that is a combination of

I am facing an issue in implementing TypeScript validation for filtering an array. I have a specific array of actions and I want to filter out internal actions from it. Despite my efforts, I am unable to properly communicate to TypeScript that the filtered ...

Unable to bind to property as it is not recognized as a valid attribute of the selector component

I have a situation where I need to pass a variable from one component to another using @input. Here is my parent component : @Component({ selector: 'aze', templateUrl: './aze.component.html', styleUrls: [('./aze.compo ...

Include TypeScript in a single component within an already established Vue project

I've been working on a large Vue project and I want to integrate TypeScript into it. However, every time I try to do so, I run into a lot of errors that take weeks to fix. Instead of going through that, I'd like to find a way to add TypeScript to ...

Storing application state using rxjs observables in an Angular application

I'm looking to implement user status storage in an Angular service. Here is the code snippet I currently have: import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; @Injectable() expo ...

Lazy-loading modules in SSR Angular 8 applications are currently unspecified

I am currently in the process of setting up my Angular 8 application to work with server-side rendering (SSR). However, I am encountering some undefined errors in webpack when running my application using ng serve, especially with lazy-loaded modules. Ever ...

The type definition file for '@types' is not present in Ionic's code base

After updating my Ionic 6 project to use Angular 3, everything works perfectly in debug mode. However, when I attempt to compile for production using 'ionic build --prod' or 'ionic cordova build android --prod', I encounter the followin ...