incomplete constructor for a generic class

I have multiple classes that I would like to initialize using the following syntax:

class A {
    b: number = 1
    constructor(initializer?: Partial<A>) {
        Object.assign(this, initializer)
    }
}

new A({b: 2})

It seems to me that this initialization behavior is a common pattern and I want to centralize this logic to avoid duplication in many files. I attempted the following approach:

class Initializable<T> {
    constructor(initializer?: Partial<T>) {
        Object.assign(this, initializer)
    }
}

class A extends Initializable<A> {
    b: number = 1
}

new A({b: 2})

This code compiles successfully but does not work as expected because the implicit super() call overrides the initialized value of b. Is there a type-safe solution in TypeScript to achieve this shared behavior in all my classes?

Answer №1

If you're looking to execute something from the base class after the derived class constructor has completed, it can be a bit tricky. One possible solution is to utilize a function that enhances the A class instead of relying on a base class. Essentially, this involves using a mixin approach for adding functionality.

function initializable<T extends new() => any>(cls: T) : {
    new (data?: Partial<InstanceType<T>>) : InstanceType<T>
} & Pick<T, keyof T> {
    return class extends (cls as any) {
        constructor(data?: Partial<InstanceType<T>>){
            super();
            if(data) { 
                Object.assign(this, data);
            }
        }
    } as any

}

const A = initializable(class {
    b: number = 2;
    static staticMethod() { }
    method() {}
});
type A = InstanceType<typeof A>

var a = new A({b:1});
console.log(a.b)
a.method();
A.staticMethod();
a = new A();
console.log(a.b) 
var aErr = new A({b:"1"}); //error

Note: Mix-ins are typically not allowed to modify the constructor arguments, which is why some type adjustments may be necessary for this workaround to function correctly.

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

Having Trouble with Typescript Modules? Module Not Found Error Arising Due to Source Location Mismatch?

I have recently developed and released a Typescript package, serving as an SDK for my API. This was a new endeavor for me, and I heavily relied on third-party tools to assist in this process. However, upon installation from NPM, the package does not functi ...

What is the best way to pass props to a styled component (e.g., Button) in Material-UI

One of my tasks involves creating a customized button by utilizing the Button component with styled components. export const CustomButton = styled(Button)({ borderRadius: "17px", fontWeight: 300, fontSize: ".8125rem", height: &q ...

Applying the `lean` method to Mongoose queries that retrieve arrays in TypeScript

When working with two Mongoose queries, I made the decision to utilize the .lean() method on both of them. It appears that using .lean() on a query that returns a single document works well: let something:Something; SomethingDocument.findOne({_id:theId}) ...

Navigating to the tsconfig.json file based on the location of the file being linted

In my monorepo, each package currently contains a .eslintrc.cjs file with the following setup: Package-specific ESLint Configuration const path = require('path') const ts = require('typescript') const OFF = 0 const WARN = 1 const ERROR ...

Just a straightforward Minimum Working Example, encountering a TypeScript error TS2322 that states the object is not compatible with the type 'IntrinsicAttributes & Props & { children?: ReactNode; }'

Currently, I am immersed in a project involving React and Typescript. I am grappling with error code TS2322 and attempting to resolve it. Error: Type '{ submissionsArray: SubmissionProps[]; }' is not assignable to type 'IntrinsicAttributes ...

Jest's --findRelatedTests fails to identify associated test cases

Whenever I execute the command jest --bail --findRelatedTests src/components/BannerSet/BannerSet.tsx , an unexpected message is displayed: I couldn't locate any tests and hence exiting with code 1 If you want to exit with code 0 even when there are n ...

Angular 5's data display glitch

Whenever I scroll down a page with a large amount of data, there is a delay in rendering the data into HTML which results in a white screen for a few seconds. Is there a solution to fix this issue? Link to issue I am experiencing HTML binding code snippe ...

Angular click event to compute a function and display the result on a separate page

Currently, I am developing a BMI app using Ionic Angular (latest version). The objective is to create a button that collects input data from fields and triggers a method to validate the inputs. Once validated, the app should display the results page with t ...

"Silently update the value of an Rxjs Observable without triggering notifications to subscribers

I'm currently working on updating an observable without alerting the subscribers to the next value change. In my project, I am utilizing Angular Reactive Forms and subscribing to the form control's value changes Observable in the following manner ...

Leverage Sinon's fakeServer in combination with promises and mocha for efficient

Having an issue here: I need to test a method that involves uploading data to an AWS S3 bucket. However, I don't want the hassle of actually uploading data each time I run my tests or dealing with credentials in the environment settings. That's w ...

The type 'MouseEvent<HTMLButtonElement, MouseEvent>' cannot be matched with the type 'boolean'

Just starting out with TS and running into a problem that TS is pointing out to me. Error: Type '(x: boolean) => void' is not compatible with type '(e: MouseEvent<HTMLButtonElement, MouseEvent>) => void'. Parameters ' ...

Tips for Validating Radio Buttons in Angular Reactive Forms and Displaying Error Messages upon Submission

Objective: Ensure that the radio buttons are mandatory. Challenge: The element mat-error and its content are being displayed immediately, even before the form is submitted. It should only show up when the user tries to submit the form. I attempted to use ...

Retrieving User's Theme Preference from Local Storage in Next.js Instantly

As mentioned in various other responses, such as this one, Next.js operates on both the client and server side, requiring a guard to properly fetch from localStorage: if (typeof localStorage !== "undefined") { return localStorage.getItem("theme") } else ...

NGXS Alert: Unable to resolve parameters for TranslationEditorState: (?)

I'm currently implementing NGXS for state management within my Angular 9 application. I've encountered a specific issue where any attempt at dependency injection in one of the state classes results in an error message stating "Error: Can't r ...

how can JavaScript be used to retrieve an object based on a condition from an array of objects and an ArrayList

My JavaScript challenge involves working with an array of objects called arrobj and a list called prgList. The goal is to extract the names from arrobj based on the programs listed in prgList. If the program exists in arrobj, it should be displayed accor ...

Angular date selection with a range of plus two days, factoring in the exclusion of weekends

I am currently using a mat date picker range with specific logic. The minimum date that a user can select on the calendar is set to + 2 days. For example, if today's date is July 20, 2022, the minimum selectable date would be July 22, 2022. However, ...

The child module invokes a function within the parent module and retrieves a result

Guardian XML <tr [onSumWeights]="sumWeights" > Algorithm sumWeights(): number { return // Calculate total value of all weights } Offspring @Input() onTotalWeights: () => number; canMakeChanges() { return this.onTota ...

The power of Ionic 2 combined with the Web Audio API

I am currently developing an Ionic 2 application that requires access to the user's microphone. When working on a web platform, I would typically use the following code snippet to obtain microphone access. navigator.getUserMedia = (navigator['ge ...

Design buttons that are generated dynamically to match the style

I have a challenge in styling dynamically generated buttons. I've developed a component responsible for generating these dynamic buttons. const TIMER_PRESETS: Record<string, number> = { FIFTHTEENSEC: 15, THIRTYSEC: 30, FORTYFIVESEC: 45, ...

Unable to execute the Vite project

I ran into an issue with my Vite project yesterday. I closed it and now that I have reopened it, the 'npm run dev' command is throwing an error. My project is built using Vite with React and TypeScript. Attached is a screenshot of the error mess ...