Reputation: 419
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
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
Upvotes: 4
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