Why doesn't typescript enforce readonly keyword and prevent us from passing a read only property into non readonly one, that's defy the point
let foo: {
readonly bar: number;
} = {
bar: 123
};
function iMutateFoo(foo: { bar: number }) {
foo.bar = 456;
}
iMutateFoo(foo); // The foo argument is aliased by the foo parameter
console.log(foo.bar); // 456!```
It's a known behavior, tracked at microsoft/TypeScript#13347 whose surprising effects inspired an issue originally titled "readonly modifiers are a joke". The short answer to the question is "it would have broken backwards compatibility when readonly
was introduced". The long answer comes from the following comment:
@ahelsberg said:
In order to ensure backwards compatibility, the
readonly
modifier doesn't affect subtype and assignability type relationships of the containing type (but of course it affects assignments to individual properties).Consider the following code:
interface ArrayLike<T> { length: number; [index: number]: T; } function foo(array: ArrayLike<string>) { // Doesn't mutate array } var s = "hello"; var a = ["one", "two", "three"]; foo(s); // s has readonly length and index signature foo(a);
In existing TypeScript code there is no way to indicate if a particular property is intended to be read-only or mutable. In the code above,
foo
doesn't mutate the array it is passed, but there is nothing in the code that says it can't. However, now that we've added areadonly
modifier to thelength
property and the index signature in theString
interface (because they really are read-only), thefoo(s)
call above would be an error if we said that areadonly
property is incompatible with a property withoutreadonly
. Specifically, we can't interpret the absence of areadonly
modifier to mean read-write, we can only say that we don't know. So, if an interface differs from another interface only in thereadonly
modifiers on its properties, we have to say that the two interfaces are compatible. Anything else would be a massive breaking change.
So there you have it. If you want to show your support for a fix to this, you might want to head over to that GitHub issue and give it a 👍 or describe your use case if it's compelling and not already mentioned.