microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.08k stars 12.49k forks source link

Interface with readonly property is assignable to interface with mutable property #13347

Open danielearwicker opened 7 years ago

danielearwicker commented 7 years ago

TypeScript Version: 2.1.4

Code

interface MutableValue<T> {
    value: T;
}

interface ImmutableValue<T> {
    readonly value: T;
}

let i: ImmutableValue<string> = { value: "hi" };
i.value = "Excellent, I can't change it"; // compile-time error

let m: MutableValue<string> = i;
m.value = "Oh dear, I can change it";

Expected behavior: The assignment of i to m would fail, to stop us accidentally allowing value to be modified.

Actual behavior: The assignment is allowed.

The current behaviour was a deliberate choice so this is a breaking change (or strict flag) feature request rather than a bug report!

The Handbook has this snippet:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

It notes that a = ro being an error is helpful. But this happens because ReadonlyArray has no push method, making it incompatible with Array.

My example above seems "morally equivalent" to modelling the input/output flow of values with separate methods:

interface MutableValue<T> {
    getValue(): T;
    setValue(v: T): void;
}

interface ImmutableValue<T> {
    getValue(): T;
}

declare let i: ImmutableValue<string>;
i.setValue("Excellent, I can't change it"); // compile-time error

let m: MutableValue<string> = i;
m.setValue("Oh dear, I can change it");

And sure enough, this stops the assignment of i to m.

Would be great if mutable and readonly properties had the same relationship as if they were modelled by separate get/set methods (which of course they might actually be, via property getter/setters).

aboodman commented 1 year ago

@RyanCavanaugh - can you please comment on the status of this bug from the project's perspective? I don't see any official comment from the ts team.

RyanCavanaugh commented 1 year ago

It's in the long list of features which are plausible, but aren't currently a priority based on the overall tradeoff of what would be gained here (write safety under aliasing, which is already a soundness hole as relates to the types of the properties themselves) vs what would be required for anyone to be able to turn the option on (generally speaking, "everyone" would have to properly document readonly for it to be usable in practice).

adrian-gierakowski commented 1 year ago

generally speaking, "everyone" would have to properly document readonly for it to be usable in practice

It’s a chicken and egg problem

cedw032 commented 7 months ago

This is a pretty massive hole.

What do we have to do to get some movement on this?

Does anyone know why this keyword was added when it provides next to no protection?

EDIT: To be clear, I am asking if someone could point me towards the PR or issue that led to the addition of this feature so I can establish what the intention was, and how it ended up in the state that it's in. I am sure it was done with good intentions, and it seems unfortunate that it is stuck half way.

MartinJohns commented 6 months ago

Relevant comment by RyanCavanaugh: https://github.com/microsoft/TypeScript/issues/58236#issuecomment-2065166982

We're considering closing the { readonly x: number } -> { x: number } soundness hole under a flag, [..]

ahejlsberg commented 6 months ago

Now implemented in #58296.