question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Interface with readonly property is assignable to interface with mutable property

See original GitHub issue

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).

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:200
  • Comments:27 (6 by maintainers)

github_iconTop GitHub Comments

38reactions
jtbandescommented, Sep 1, 2021

Worth noting that Flow already handles this correctly:

/* @flow */

type State = { x: number; y: string; };

function mutate(state: State) {
  state.x = 1;
}

function definitelyDontMutate(state: $ReadOnly<State>) {
  mutate(state);
//       ^ Cannot call `mutate` with `state` bound to `state` because property `y` is read-only in `State` [1] but writable in `State` [2]. [incompatible-variance]
}

I’d be in favor of an optional flag like --strictReadonlyChecks to opt-in to stronger type checking for those who are willing to wade through some breaking changes.

18reactions
EisenbergEffectcommented, May 9, 2018

I’d love to see if there’s any progress on this. I was just teaching some engineers about TypeScript today, showing different aspects of interfaces, optional and readonly properties. I went a little off my script and showed something like above…which didn’t work how I would have expected.

I’d love to see the readonly constraint respected. It’s particularly relevant for some of the work we’re doing on the vNext of Aurelia, which is all TypeScript. We also have a lot of immutable scenarios in my day job’s codebase where it would be nice to get some help from the compiler…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Change a readonly property to mutable in TypeScript
You can use mapping modifiers to change a readonly property to mutable in TypeScript, e.g. `-readonly [Key in keyof Type]: Type[Key]`.
Read more >
How can I ensure that a readonly property is NOT assignable ...
The non-assignability of MyImmutableArrayThing s to MyArrayThing is key to the type system enforcing the properties we want, but how do we stop ......
Read more >
Find out how to make readonly properties mutable in TypeScript
You can create a "mutable" utility type in the following way: // TypeScript 2.8+ type Mutable<T> = { -readonly [P in keyof T]:...
Read more >
The "readonly" lie of Typescript - Banterly
interface UserInfo { readonly name : string, readonly phone: string, ... assign to 'name' because it is a read-only property user.phone ...
Read more >
Handbook - Interfaces - TypeScript
Not all properties of an interface may be required. ... type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found