What is the most graceful method to define a class attribute just once?

Is there a clean and efficient way to set a value only once within a TypeScript class? In other words, is there a way to make a value read-only after it has been assigned?

For instance:

class FooExample {
    public fixedValue: string;

    public setFixedValue(value: string): void {
        if (!this.fixedValue) {
            // this value should be immutable
            this.fixedValue = value;
        }
    }
}

I am specifically not interested in using a getter method, as the property may still need to be changed internally within the class.

Answer №1

After carefully reviewing various answers and conducting thorough research, I have identified what I consider to be the most refined solution. The approach involves defining the value as an Object and then using the freeze() method to lock it after setting the necessary property.

class BarExample {
    public lockedValue = {
        data: '';
    };

    public setLockedValue(data: string): void {
        if (!Object.isFrozen(this.lockedValue)) {
            this.lockedValue.data = data;
            Object.freeze(this.lockedValue);
        }
    }
}

Answer №2

To achieve this goal, one can utilize a "smart" setter approach. The concept involves defining a setter within the class, removing the setter from within the class itself, and creating a read-only property using the Object.defineProperty method with a descriptor where the writable property is set to false.

The aspect of being "changed within the class itself" can be accomplished through redefining another property. It is important to ensure that the property is initially made configurable.

Below is an example of how this process could look (there might not be a more elegant solution) - note that this runtime version of the answer is not specific to TypeScript:

class ReadonlyFields {
  set fixed(value) {
    Object.defineProperty(this, "fixed", {
      value,
      writable: false,
      configurable: true,
    });
  }

  doSomething() {
    Object.defineProperty(this, "fixed", {
      value: 24, //exact logic for changing the value is up to you
      writable: false
    });
  }
}

const r = new ReadonlyFields();
r.fixed = 42;
console.log(r.fixed); //42;
r.fixed = 24;
console.log(r.fixed); //42;
r.doSomething();
console.log(r.fixed); //24;

If enforcing this behavior from a type system perspective is desired, simply make the property readonly:

class ReadonlyFields {
  readonly fixed!: number;

  setFixed(value: number) {
    Object.defineProperty(this, "fixed", {
      value,
      writable: false,
      configurable: true,
    });
  }

  doSomething() {
    Object.defineProperty(this, "fixed", {
      value: 24, //exact logic for changing the value is up to you
      writable: false,
    });
  }
}

const r = new ReadonlyFields();
r.setFixed(42); //OK
//@ts-expect-error
r.fixed = 24;
r.doSomething(); //OK

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

Gathering user key event input for a duration of 2 seconds before resetting it

I need help implementing a feature where I can clear the user's input text after 500ms if they are entering characters consecutively. private userInputTimer; private userInputText = ''; private handleEvent(event: KeyboardEvent): void { if ...

Updating the status of various sections with Redux toolkit

Is it possible to update the state of a store, which consists of multiple slices, with a new state in React using Redux Toolkit? While you can revert the entire store to its initial state using extraReducers, is it also possible to change the state to som ...

How can you partially update an entity in TypeORM using a query runner?

Updating a user's name partially is simple with the repository save method: await this.repository.save({ id: 1, name: 'john' }); However, when attempting to do the same using queryrunner, all fields must exist which throws an error stating ...

TypeScript excels in typechecking when using two individual assignments, but may encounter issues when attempting typechecking with tuple

I am quite puzzled by a discovery I made and I am seeking to understand why TypeScript is displaying this behavior and what the underlying reason may be. Here is the code snippet: class A { constructor(public name : String, public x = 0, public y = 0) ...

Guide to Rolling a Set of 5 Dice

I am looking to develop a game involving 5 dice. I have already created a function to roll one die using a random method, but I am unsure how to extend this functionality to the remaining four dice without having to create a separate method for each one. ...

Is there a method to implement retries for inconsistent tests with jest-puppeteer?

Currently, I am struggling with an issue where there is no built-in method to retry flaky tests in Jest. My tech stack includes Jest + TypeScript + Puppeteer. Has anyone else encountered this problem or have any suggestions for possible solutions? I attem ...

The attribute 'subtle' is not found within the definition of 'webcrypto' type

Currently, I am working with Node v17.4 and I am looking to utilize the webcrypto API. Referencing this specific example, I am attempting to include subtle in my project, but TypeScript is throwing an error: Property 'subtle' does not exist on ...

webpack is having trouble compiling TypeScript with React components

I've been working on setting up a TypeScript React project with webpack. I followed the TypeScript Tutorial, but I keep running into an error message that says `module parse failed: ... you may need an appropriate loader` Interestingly, I can success ...

Using the js-cookie library in a TypeScript-based project: A guide

Looking to incorporate the js-cookie library into my TypeScript project. After installing the library and typings with the command npm install js-cookie @types/js-cookie --save-dev in the location containing node_modules and package.json, my package.json ...

How can I ensure that a user variable stored in an Angular6 service remains defined and accessible from other components?

Currently, I am working on an Angular app and facing a challenge. After receiving a user variable from an asynchronous call to Firestore Cloud, I noticed that the variable is successfully set (verified using console.log()). However, when I navigate between ...

Show the interface value for an array type

I have created a component to display API data. The structure of the component is as follows: HTML: <div *ngFor="let customer of customers"> <p>Name: {{customer?.name}}</p <p>Phone: {{customer?.phoneNumbers}}</p </div&g ...

Enhance your UI experience with a beautifully styled button using Material-

I was using a Material UI button with a purple background. <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', heig ...

Can Typescript classes be hoisted if I use two classes in my code?

Exploring Class Definitions Certain Rules to Comply With Ensuring that the class is defined in advance helps avoid errors. class Polygon { log() { console.log('i am polygon'); } } const p = new Polygon(); // Expected: no errors p.log(); U ...

Creating a TypeScript function that automatically infers the type of the returned function using generics

Suppose I want to execute the generateFunction() method which will yield the following function: // The returned function const suppliedFunction = <T>(args: T) => { return true; }; // The returned function // This is how it can be used suppli ...

The content of the string within the .ts file sourced from an external JSON document

I'm feeling a bit disoriented about this topic. Is it feasible to insert a string from an external JSON file into the .ts file? I aim to display the URLs of each item in an IONIC InAppBrowser. For this reason, I intend to generate a variable with a sp ...

Is there a way to convert a JSON input object to a model class using TypeScript in a Node.js application?

Currently, I am developing my Node.js server using TypeScript and the express framework. Here is an example of what my controller and route looks like: export class AuthController { public async signUpNewUser(request: Request, response: Response) { ...

Is it feasible to stop closure from capturing external variables in TypeScript?

Imagine I have the following piece of Typescript code: const a = 456 const b = () => a Is there a way to make the Typescript compiler detect and flag an error or warning during compilation indicating that function b is accessing an external variable a ...

TypeScript's type inference feature functions well in scenario one but encounters an error in a different situation

I recently tried out TypeScript's type inference feature, where we don't specify variable types like number, string, or boolean and let TypeScript figure it out during initialization or assignment. However, I encountered some confusion in its be ...

Stringified HTML code showcased

When working with Angular, I have encountered an issue where I am calling a function inside an .html file that returns a string containing a table element. My goal is to convert this string into HTML code. I attempted to use [innerHtml]: <p [innerHtml ...

Compiling TypeScript files with an incorrect path when importing, appending "index" at the end of the @angular/material library

I'm currently working on creating a library to collect and distribute a series of Angular components across various projects, with a dependency on angular/material2. My objective is to eventually publish it on npm. However, I've encountered an i ...