Constructor caching leading to the error message: "The property '...' does not have an initializer and is not definitely assigned in the constructor."

Whenever I work on projects, I tend to create classes with constructors that cache the objects they generate. This way, if the constructor is called with the same parameters multiple times, it will return the same instance rather than creating redundant instances.

Let's take a look at a simple example:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    readonly name: string; // A compilation error occurs here.

    constructor(name: string) {
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        this.name = name;
        X.__cache[name] = this;
    }
}

This code used to work flawlessly in TypeScript until I upgraded to version 2.7 and enabled strictPropertyInitialization. Now, an error pops up on readonly name: string; stating:

Property 'name' has no initializer and is not definitely assigned in the constructor.

I have several classes in my projects following this pattern, so I need to come up with some general solutions to eliminate the error.

Two solutions I am not keen on:

  1. Disabling strictPropertyInitialization. Although useful, I prefer not to turn it off as it revealed areas needing improvement and forced me to update definitions in some cases.

  2. Adding a definite assignment assertion to name, like readonly name!: string;. While this bypasses the error, it also weakens compiler checks. If I accidentally miss the assignment this.name = name, TypeScript won't flag it as an error - which is risky for catching mistakes early.

The example provided was minimal, but in actual applications, classes may have numerous fields or involve complex computations rather than simple parameter assignments.

Answer №1

When dealing with cases where calculating the object's fields is a resource-intensive task, my preferred solution involves marking the constructor as private (although protected might be appropriate in certain scenarios) and defining a factory function as a static member within the class. Here's an example:

class X2 {
    private static __cache: Record<string, X2> = Object.create(null);

    readonly name: string;

    private constructor(nameSource: string) {
        this.name = expensiveComputation(nameSource);
    }

    // Instead of directly using `new X2`, we utilize this factory function to instantiate new objects.
    static make(name: string): X2 {
        const cached = X2.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        return X2.__cache[name] = new X2(name);
    }    
}

Since the constructor consistently assigns values to all fields, TypeScript no longer encounters any issues. This approach necessitates that when working with the class, the factory function must be employed for creating new instances instead of directly invoking the constructor.

Answer №2

In scenarios where my constructor simply assigns parameters to fields without extensive computation, I have found a different solution. I prefer flipping the logic in the constructor to first perform field assignments before checking if an instance already exists. Although this may result in unnecessary field assignments if an instance is already present, I only address this concern if profiling indicates it poses a significant issue in a practical application.

The revised approach looks like this:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    readonly name: string;

    constructor(name: string) {
        // Assigning the fields first...
        this.name = name;

        // Then determining if an instance exists for return.
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        X.__cache[name] = this;
    }
}

If utilizing parameter properties, the code can be further simplified to:

class X {
    private static __cache: Record<string, X> = Object.create(null);

    // The constructor parameter doubles as the property definition on X instances.
    constructor(readonly name: string) {

        // Determining if an instance is available for return.
        const cached = X.__cache[name];
        if (cached !== undefined) {
            return cached;
        }

        X.__cache[name] = this;
    }
}

Answer №3

Visit this link to learn how to fix the TypeScript error in Visual Studio 2017

When facing issues within our own code, it is important to manually add the definite assignment assertion modifier as mentioned in the provided link. By placing a few ! in the right places, we can effectively resolve any problems in our project.

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

Chai expect() in Typescript to Validate a Specific Type

I've searched through previous posts for an answer, but haven't come across one yet. Here is my query: Currently, I am attempting to test the returned type of a property value in an Object instance using Chai's expect() method in Typescript ...

Running a function before triggering a refresh in Angular 2/4

When a user clicks or presses the F5 button on an HTML page, the page refreshes. However, before the refresh occurs, I want to execute a function or display a simple alert. The user can trigger a refresh by clicking the refresh button, pressing F5, or usi ...

The Error message "Property 'data' is not present in Type <void> | AxiosHttpResponse<any>" is indicating that the data property is missing on

When I fetch data for a specific user, I have a promise that I use to setState. Below is the implementation: getUserUsername = (): string => { const { match } = this.props; return match.params.username; }; onFetchUser = () => getUse ...

Is there a deeper philosophical rationale behind choosing to use (or not use) enums in TypeScript, along with string union types?

Recently, I delved into the world of enum and const enum in Typescript, causing some confusion. I grasped that const enum gets transpiled into simple values while regular enums do not. I also recognized certain distinctions between using string union type ...

Error encountered while compiling an Asp.Net Core project due to exceeding the maximum allowable path length in the

Encountering a critical error during the build process with Visual Studio 2016 update 3 Asp.Net Core. The build is interrupted with the following message: Severity Code Description Project File Line Suppression State Error MSB4018 The "FindC ...

Is it possible for TypeScript to manage a dynamic return type that is not determined by a function parameter?

I am facing a challenge with dynamic type checking using a param type and seeking help to solve it. Even though it might be a difficult task, any assistance would be greatly appreciated! Consider the following code: class DefaultClass { defaultProp: n ...

Leveraging Typescript Definitions Files from Definitely Typed with an Outdated Typescript Version

I've been struggling with integrating third party React component libraries into my project that uses Typescript 1.8.10 along with React and Redux. Specifically, I've been attempting to use React Date Picker, but have encountered issues due to th ...

The specified JSX element type (whether it be from react-konva or not) does not contain any defined constructors or callable signatures

I recently switched over to TypeScript in React and started using Konva library. When I updated my "@types/react" package from version "^17.0.39" to "^18.0.1", I encountered an error message that appears on every Konva component. ...

Received corrupted file during blob download in Angular 7

When using Angular 7, I am making an API call by posting the URL file and attempting to download it using the 'saveAs' function from the fileSaver library. The file is successfully downloading, but it appears to be corrupted and cannot be opened. ...

Tips for specifying a clear type in TypeGraphQL when dealing with key-value pair objects

In my efforts to define explicit types in type-graphql for key-value pairs, I am encountering an issue. I have declared the key-value pair using [key: string]: string, indicating that the keys can be of any string type and the values must be strings. This ...

CORS policy blocked Axios React GET request

I am attempting to utilize Axios for fetching an API from in order to receive a response. I am currently facing difficulties in resolving the encountered issue. My Axios call is simple and located in the following file: api/index.js import axios from &q ...

Typescript array iteration using dual parameters

I seem to be struggling with the logic behind this seemingly straightforward iteration question. My task involves iterating through an array of data based on id and code, removing data only when the code is not associated with the given id's. Let&ap ...

Ways to efficiently update the API_BASE_URL in a TypeScript Angular client generated by NSwag

Is it possible to dynamically change the API_BASE_URL set in my TypeScript client generated by NSWAG? I want to be able to utilize the same client with different API_BASE_URLs in separate Angular modules. Is this achievable? Thank you for your assistance. ...

Why does the property of {{hero.name}} function properly in a <h> tag but not in an <img> tag?

Within this template, the code below functions correctly: <h3>{{hero.name}}</h3> It also works for: <a routerLink="/details/{{hero.id}}">{{hero.name}}</a> However, there seems to be an issue with the following image path ...

Updating parent components through child components in ReactWould you like another unique

In my current project, I am attempting to change the state of the main component labeled App.tsx by using a child component called RemarksView.tsx. I have attempted passing props such as setRemarks and remarks, but unfortunately the state in the parent c ...

Saving large amounts of data in bulk to PostgreSQL using TypeORM

I am looking to perform a bulk insert/update using TypeORM The Test entity is defined below: export class Test { @PrimaryColumn('integer') id: number; @Column('varchar', { length: 255 }) testName: string; } I have the f ...

Create a Jest mock for a namespace and a function that have the same name

The structure of a library I'm currently using is as follows: declare namespace foo { function bar(); }; declare namespace foo.bar { function baz(); }; My task involves mocking the functions foo.bar() and foo.bar.baz(). To mock foo.bar(), ...

Enhance Material UI with custom properties

Is it possible to add custom props to a Material UI component? I am looking to include additional props beyond what is provided by the API for a specific component. For example, when using Link: https://material-ui.com/api/link/ According to the document ...

Suggestions for efficiently filtering nested objects with multiple levels in RXJS within an Angular environment?

Just a Quick Query: Excuse me, I am new to Typescipt & RxJS. I have this JSON data: [ { "ID": "", "UEN": "", "Name": "", "Address": "", "Telephone&quo ...

Organizing data in TypeScript

Is there a way to alphabetically sort this list of objects by name using TypeScript? "[{name:"Prasanna",age:"22",sex:"Male",Designation:"System Engineer",Location:"Chennai"}, {name:"Nithya",age:"21",sex:"Female",Designation:"System Engineer",Location ...