Requiring Subclasses to Maintain Immutability

I have created a base class that contains various properties:

class Component {
    readonly id: number
    readonly type: number
}

Now, I am looking to create some subclasses, such as:

class HealthComponent extends Component {
    max_health: number,
    current_health: number
}

etc.

My goal is for HealthComponent to exhibit the same behavior as an Immutable.Record:

const health = HealthComponent(100, 100);
health.max_health = 40; // This should not work
const new_health = new HealthComponent(40, health.current_health); // This works

All of these classes are strictly data-oriented with no behavioral methods. To ensure immutability, I want to prevent modifications and instead return a new object or throw an error similar to how it's done in Immutable.js. However, I am struggling to find the best approach to achieve this.

The approach I'm currently considering involves giving each subclass a readonly data property that is an Immutable.Record containing the relevant fields. Yet, even this solution falls short since modifying it would only result in a new data object rather than an entirely new Component object. Moreover, this method does not guarantee consistency across all subclasses.

Another option under consideration is making the base class an Immutable.Record with a data: Immutable.Map field, while requiring subclasses to provide an Immutable.Map to the super constructor with all necessary keys. Unfortunately, this setup leaves room for unrestricted addition of new keys.

Are there any design patterns available that could assist me in achieving my intended functionality?

Answer №1

Utilize the Readonly feature with a mapped type.

class Item {
    constructor(public itemId: number, public itemType: number) {

    }
}

class BookItem extends Item {
    constructor(public itemId: number, public itemType: number, public pageCount: number, public currentChapter: number) {
        super(itemId, itemType);
     }
}

let book: Readonly<BookItem> = new BookItem(1, 2, 300, 10);
book.pageCount = 400; // Results in an error

If there is no specific behavior required, consider incorporating an interface.

interface Item {
    itemId: number;
    itemType: number;
}

interface BookItem extends Item {
    pageCount: number;
    currentChapter: number;
}

let book: Readonly<BookItem> = {
    itemId: 1,
    itemType: 2,
    pageCount: 300,
    currentChapter: 10
};

book.pageCount = 400; // Generates an error

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

Using Angular JS to retrieve specific information from a JSON file

I am in the process of developing an AngularJS app that showcases notes. The notes are stored in a note.json file, and the main page of the app only displays a few fields for each note. I want to implement a feature where clicking on a specific note opens ...

What is the proper way for the curry function to function effectively?

Here's a function that I came across: function curry(fn) { var args = [].slice.call(arguments, 1); return function() { return fn.call(this, args.concat([].slice.call(arguments))); }; } I always thought this was the correct way fo ...

Using a render target causes certain elements of my visual graphics to become hidden

Hey there, I've been experimenting with render targets lately and encountered some issues. I've put together a simplified example below: init = function() { // RENDERER canvas = document.getElementById("mycanvas"); renderer = new THREE ...

How can I establish default values for 2 to 3 options in a Dropdownlist?

Is there a way to set two values as default in a dropdown list, and when the page is refreshed, the last two selected values are retained as defaults? Thanks in advance! Visit this link for more information ...

The object literal is restricted to defining existing properties, and 'id' is not a recognized property in the type 'FindOptionsWhere<Teacher>'

I encountered a persistent error within my teachers.service: Object literal may only specify known properties, and 'id' does not exist in type 'FindOptionsWhere | FindOptionsWhere[]'.ts(2353) FindOneOptions.d.ts(23, 5): The expected t ...

Incrementally including DIV elements...one by one...2, 4, 6, 8, 10, 12

I am in the process of creating animated div boxes step by step that are activated when a button is pressed. The current code I have is working smoothly, with the box moving right and then left back to its original position. You can see a demo of this here ...

Show angular variable data in a table based on their index

I have an array of angular variables with values like [ 170, 1, 130, 3, 134, 4.... and so on]. I would like to present these values in a table format where the even positioned values are shown in one column and the odd positioned values in another column. ...

What is the best way to access individual items within an *ngFor loop in Angular?

Is it possible to retrieve the value of item.profile image and utilize it in the update function shown in the code below? <ion-content> <ion-grid style ="text-align: center"> <ion-row class="ion-align-items-center" > ...

How to upload a document to Alfresco using JavaScript API

I am facing an issue while attempting to upload a file from a node application to my local Alfresco server. I have been able to login, create, and delete folders successfully, but I am encountering difficulties when trying to upload files. let AlfrescoApi ...

Accessing a Global Variable within a Node Express Class Module

Within my Node Express application, I defined a variable in app.js like so: let accounts = [checkingAccount, savingAccount] Now, I have a class file named account.js (not a route) and I need to access the accounts array from within it. How can I achieve t ...

Trouble with triggering events from Datatable buttons

Below is the code snippet I am currently using for my DataTable : var oTable12= $('#example').DataTable({ "aaData": tableData, "aLengthMenu": [[5, 10, 20, -1], [5, 10, 20, "All"]], "iDisplayLength": 5, "aoColumnDefs ...

Stunning Opening and Closing Animation with Ajax

Looking for help with creating an animation like the one shown here: Incorporating this into my current site at: dageniusmarketer.com/DigitalWonderland/ I want the window displaying text content to open and close as users navigate through the links, ess ...

How can you correctly make an Ajax request using computed properties sourced from VueX Store?

Is there a way to make an AJAX call where one of the parameters is a computed State in VueX? For instance, if I use this.$axios.get('someUrl/' + accID ), with accID being a computed property from VueX (using MapState), sometimes it returns ' ...

Angular Pagination: Present a collection of pages formatted to the size of A4 paper

Currently, I am working on implementing pagination using NgbdPaginationBasic in my app.module.ts file. import { NgbdPaginationBasic } from './pagination-basic'; My goal is to create a series of A4 size pages with a visible Header and Footer onl ...

Transferring a JSON-encoded string in "windows-1251" format from Python to JavaScript

What I need help with can be best exemplified with a code snippet. Before, I had the code below: content = u'<?xml version="1.0" encoding="windows-1251"?>\n' + ... # with open(file_name, 'w') as f: f.write(content.enco ...

stop all HTML5 videos when a specific video is played using jQuery

I am facing an issue with 10 HTML5 videos on the same page, CHALLENGE Whenever I click on different videos, they all start playing simultaneously... <div><video class="SetVideo"></video><button class="videoP"></button>< ...

Animating the Three.js Globe camera when a button is clicked

Struggling to create smooth camera movement between two points, trying to implement the function below. Currently working with the Globe from Chrome Experiments. function changeCountry(lat, lng) { var phi = (90 - lat) * Math.PI / 180; var theta = ...

Maximizing the efficiency of threejs through combining and selecting items

In my exploration of three.js optimization, I discovered that reducing the number of draw calls is crucial for improving performance. One way to achieve this is by consolidating geometries through the use of GeometryUtils.merge. Although this optimization ...

Autodesk Forge serves as a hub for hosting dependencies on a nearby server

Our company has implemented an Angular/TypeScript version of the Forge Viewer, currently loading dependencies from Autodesk's server. To ensure these files are always accessible to our customers, we want to host them on our own servers. However, attem ...

What is the best way to retrieve upload progress information with $_FILES?

HTML: <input type="file" value="choose file" name="file[]" multiple="multiple"/><br/> <input type="submit" class="submit" value="confirm" /> <input type="hid ...