Understanding the concept of mutable properties in Typescript

Why can the property 'name' in the class 'PersonImpl' be reassigned even though it is not declared as read-only in the Person interface?

    interface Person {
    readonly name: string;
}

interface Greeting extends Person {
    greet(message: string): void;
}

class PersonImpl implements Greeting {
   name: string;
   age = 30;
   constructor(n: string) {
      this.name = n;
   }
   greet(message: string) {
      console.log(message + ' ' + this.name);
   }
} 

let pers = new PersonImpl("maha");
console.log(`pers : ${pers.name}`);
pers.name = "maha2";
pers.greet("hello"); //hello maha2

Answer №1

Two key points to consider: Class properties do not inherit typings from classes or interfaces they extend; and readonly properties can be mutually assigned to mutable properties.


Inheriting Typings in Class Properties

When you specify class Foo implements Bar {...}, the compiler does not automatically infer types for Foo's properties based on Bar. Instead, it independently infers the types of Foo's properties and checks them against Bar. This behavior can lead to unexpected results as shown in the following example:

interface Foo {
  x: 0 | 1;
}

class Bar implements Foo {
  x = 1; // error! x is inferred as number, not 0 | 1 
}

This discrepancy arises because x would be inferred simply as number instead of the expected 0 | 1. This issue has been a point of contention among developers, with ongoing discussions requesting improvements. You can refer to microsoft/TypeScript#10570 for more details.

For instance, the name property of PersonImpl is identified as type string rather than being readonly:

class PersonImpl implements Greeting {
  name: string; // string, not readonly string
  //...
}

Readonly Properties Compatibility

An additional issue surfaces when properties only differ in their readonly status. The TypeScript compiler considers types like {readonly x: string} and {x: string} to be interchangeable without any errors. To delve deeper into this behavior, you can check out this SO answer or view the feature request at microsoft/TypeScript#13347.

Hence, the compiler does not raise an error when PersonImpl's name property allows mutation despite Greeting's readonly specification. These types are deemed compatible by TypeScript.


Combining these factors contributes to the peculiarities observed here. Despite assuming that PersonImpl's name property is readonly, assignments may still occur, causing confusion.

To address this issue and circumvent the inherent limitations of class property inheritance, it's recommended to provide explicit type annotations and modifiers for your properties:

class PersonImpl implements Greeting {
  readonly name: string; // annotate with readonly
  /* ... */
}

let pers = new PersonImpl("maha");
pers.name = "maha2"; // error

Playground link to code

Answer №2

Why is the property name in the PeronsImpl class not read-only?

The reason for this is that it is typecast as PersonImpl rather than Person. If you wish to make name read-only, you must also enforce this within the implementation or define the variable pers as type Person.

class PersonImpl implements Greeting {
   readonly name: string; // <-- added readonly
   age = 30;
   constructor(n: string) {
      this.name = n;
   }

Alternatively, you can specify the type:

let pers: Person = new PersonImpl("maha"); // <-- added :Person

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

useEffect runs endlessly

Currently, I am using React with hooks to handle API calls and implement autoscroll functionality on a data-heavy screen. However, I have encountered a problem where the autoscroll feature implemented through a separate useEffect is interfering with the ot ...

What's causing ng-show to malfunction in IE11 on AngularJS?

I am experiencing a strange issue with my code - ng-show works perfectly fine on Firefox, but not on IE 11. <div ng-show="isExist" class="panel panel-default"> Here is the relevant code snippet from the controller: $scope.isExist = false; if(user ...

Is it a mistake? Using React and ES6 without Babel may not be the

Have you ever considered bundling all of your classes into a single file without using Babel to polyfill it to ES5? If the browser doesn't support ES6, you could then use Babel in the browser or load the polyfilled bundle and manually add the dependen ...

When trying to append in jQuery, the error "JQuery V[g].exec is not a

Attempting to create a script that adds a table to a div within the website using the following function: function generateTable(container, data) { var table = $("<table/>"); $.each(data, function (rowIndex, r) { var row = $("<tr/>"); ...

The function onReady() fails to trigger the execution of $.getJSON() upon page restoration in the browser

Initially, I want to mention that the code below functions perfectly when I launch a new browser tab and enter my web server's URL. It also works fine when I reload the page (using F5 or Ctrl-R). However, it only partially works if I reopen a closed b ...

Having a parameter that contains the characters '&' and '&' can potentially disrupt an AJAX call

Even though there is a similar question here: Parameter with '&' breaking $.ajax request, the solutions provided do not apply to my specific issue. This is because both the question and answers involve jQuery, which I am not familiar with. I ...

What is the best way to fetch images from a JSON object in a React application?

I have been trying to load images from an array of objects in React, but I keep encountering a "module not found" error. This issue has been frustrating me for the past couple of days. Can someone please help me troubleshoot this problem or provide some su ...

Extracting the value from a Text Editor in React Js: [Code snippet provided]

Currently, I am in the process of developing a basic app that generates a JSON form. So far, I have successfully incorporated sections for basic details and employment information. The basic details section consists of two input fields: First Name and Las ...

Creating a distinctive vue form component from scratch

I have a requirement to develop a Vue component that enables users to create or edit a mailing address. The existing component structure is as follows: <template> <v-container> <v-form ref="form" lazy-validation> <v-text-field ...

The functionality of Bootstrap toggle ceases to operate properly following an AJAX content update

I am currently using AJAX load to fetch some content on my webpage. I am working with Bootstrap 3 and Bootstrap toggle. When the content is loaded, the Bootstrap 3 content functions properly (the panel-primary panel is clearly visible). However, the Bootst ...

use element ui tree and vue to filter files according to selected folder

Utilizing the element UI treeview to showcase folders. Each folder or its child folder contains files that need to be displayed based on folder selection. While it's easy to filter and list out these files in a normal list, I am facing challenges with ...

The language is being detected, but the translation feature is not functioning properly with i18n

I've configured the i18n middleware in my Express Node js server as follows: // server.js import i18nMiddleware from 'i18next-express-middleware'; import i18n from 'i18next'; import Backend from 'i18next-node-fs-backend'; ...

Customized slider in jQuery UI allowing users to select height using a scaled ruler

Currently, I am tackling a challenging math problem related to adjusting the height selector input. Essentially, I have implemented a jQuery UI slider for choosing a height. It operates in inches and ranges from 0 to 120 (equivalent to 10 feet in height). ...

Divide material-ui toolbar into separate left and right sections

Is there a way to split the material-ui toolbar into a left and right part? I want to display the numSelected on the left side of the toolbar, and the delete button and edit button on the right side. Currently, my output shows these buttons just beside t ...

Laravel route does not receive a parameter sent via Ajax

I am currently using Laravel 5.8 and implementing a discount code system on my website. To achieve this, I attempted to send data via Ajax in the following manner: $.ajax({ type: 'POST', url: baseurl + 'discount/register', ...

Alert: Prop type error encountered - The prop 'open' must be defined in Snackbar component

Recently, I've been implementing jest snapshot tests into my application. The main focus is on the LoginForm component. render() { return ( ... <DynamicSnack dialogOpen={this.props.dialogOpen} snackOpen={this.props.sna ...

Angular Kendo dropdownlist and input textbox are not working together as anticipated

I'm looking to implement a dropdown list similar to Google search using Kendo Angular. However, I've encountered an issue where entering text in the textbox and pressing "Enter" passes the first matching value from the dropdown list to my compone ...

How can you determine if a polymer element has been loaded or not?

element, I am interested in dynamically importing elements using the Polymer.import( elements, callback ) method. The callback is triggered only if the elements have not been imported yet, indicating they are already loaded. My query is: Is there a conve ...

Utilizing the CSS 'overflow: hidden' property and jQuery to restrict users from scrolling during a loading page

OBJECTIVE I aim to restrict the user from scrolling while the page is loading. ISSUE The snippet of code provided successfully prevents the user from scrolling during the additional 2 seconds of the loader animation: $('body').toggleClass(&ap ...

Discover the basics of incorporating libraries using npm

As a beginner in JavaScript, I am looking to incorporate moment.js or another library into my project. However, I am unsure of how to properly set up my project so that I can import from the library. Here is how I have structured my HTML: <!DOCTYPE html ...