This usage is very simple: as a contract, the function doSomething declares it doesn't mutate the received parameter.
interface Counter {
  name: string
  value: number
}
function doSomething(c: Readonly<Counter>) {
  // ...
}
let c = {
  name: "abc",
  value: 123
}
doSomething(c)
// Here we are sure that 'c.name' is "abc" and 'c.value' is '123'
With this code:
interface Counter {
  readonly name: string
  readonly value: number
  inc(): number
}
function counterFactory(name: string): Counter {
  let value = 0
  return {
    get name() {
      return name
    },
    get value() {
      return value
    },
    inc() {
      return ++value
    }
  }
}
We have here a member readonly value that cannot be modified directly from the outside. But a member inc() can mutate the value. Also, the member value is declared as readonly but its value is changing.
I would like to know if this use of readonly on the member value is a good way to proceed. The syntax is OK. But is this example semantically correct in TypeScript? Is that what the modifier readonly is for?
The readonly keyword on a property does not ensure that the property value is constant. There's no TypeScript equivalent for that. The only things we can be sure with a readonly property are:
counterFactory return type is not defined, it is inferred exactly like the Counter interface, see (A)  in the code below.
Code example:
// (A) Usage 2 using type inference
const counter = counterFactory('foo');
type Counter = typeof counter; // Produce the exact same type as the previous `Counter` interface
counter.value = 10; // [Ts Error] Can not assign to 'value' because it is a constant or read-only property
// (B) Readonly property initialization
// (B1) With an object literal + interface 
const manualCounter: Counter = { name: 'bar', value: 2, inc: () => 0 };
manualCounter.value = 3; // [Ts Error] Can not assign...
// (B2) With a class
class Foo {
  constructor(public name: string, public readonly value: number) { }
  inc() {
    return ++this.value; // [Ts Error] Can not assign...
  }
}
const foo = new Foo('bar', 3);
foo.value = 4; // [Ts Error] Can not assign...
// (C) Circumvent TypeScript
Object.assign(foo, { value: 4 }); // → No error neither in Ts, nor at runtime
It's really confusing because it's almost like a constant property! The usage 2 and the case C prove it's not.
What creates the confusion is the "double" usage of the word `value' in your factory function.
To clarify, rewrite it this way (please note the _ in front of the _value variable):
interface Counter {
    readonly name: string
    readonly value: number
    inc(): number
}
function counterFactory(name: string): Counter {
    let _value = 0
    return {
        get name() {
            return name
        },
        get value() {
            return _value
        },
        inc() {
            return ++_value
        }
    }
}
_value is just a local var than you can mutateget value() implements the interface definition readonly value: number:  counter.value is readonlyIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With