user2285831
user2285831

Reputation: 419

Why property in class is not read only (Typescript)

Why property name in class PeronsImpl is not read only and we can reassign it the object created ?

    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

Upvotes: 2

Views: 1547

Answers (2)

jcalz
jcalz

Reputation: 327624

There's two parts to this: Class properties do not inherit typings from the classes or interfaces they extend; and readonly properties are mutually assignable to and from mutable properties.


Class properties do not inherit typings from the classes or interfaces they extend

When you write class Foo implements Bar {...}, the compiler does not use Bar to infer the types of any of Foo's properties. It behaves as if implements Bar were not present, infers all the types of Foo's properties, and then just checks against Bar. For example:

interface Foo {
  x: 0 | 1;
}

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

This is an error because class Bar { x = 1 } would infer just number for x, and since number is not assignable to 0 | 1, it's an error. This behavior is surprising and unpleasant to lots of folks, and there are quite a few GitHub issues requesting something better here. See microsoft/TypeScript#10570 for one particularly longstanding open issue about it.

That means the name property of PersonImpl is of type string, and is not readonly:

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

Readonly properties are mutually assignable to and from mutable properties

The second issue is that types which differ only by the readonly-ness of their properties are considered to be mutually compatible by the compiler. You can assign a {readonly x: string} to an {x: string} and vice versa without error. See this SO answer for more information about why that is, or see microsoft/TypeScript#13347 for a feature request asking this to be changed.

So that means the compiler does not issue an error that PersonImpl's name property is mutable while Greeting's is readonly. Those types are not considered incompatible.


Put those together, and you get the weirdness you see here. You might happily assume that PersonImpl's name property is readonly, and then get confused when you are allowed to assign a value to it.

The fix here is the general workaround to the class inherited typing issue; give explicit type annotations and modifiers to 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

Upvotes: 4

Igor
Igor

Reputation: 62213

Why property name in class PeronsImpl is not read only...

Because it is typecast as PersonImpl and not Person. If you want name as readonly you must also make it so in the implementation or variable pers should be defined as type Person.

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

or specify type

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

Upvotes: 1

Related Questions