Typescript: Suggestion for readonly interface

Created on 11 Jan 2018  Â·  7Comments  Â·  Source: microsoft/TypeScript

Avoid duplicating "readonly" for each property by applying it to the interface definition itself.
This is to reduce boilerplate code defining immutable objects.

Having interface where ALL properties are read only:

interface State {
    readonly prop1: string;
    readonly prop2: string;
    ...
    readonly prop22: string;
}

equals to:

readonly interface State {
    prop1: string;
    prop2: string;
    ...
    prop22: string;
}
Awaiting More Feedback Suggestion

Most helpful comment

const interface might be nice for deep readonly (kinda like { a: { b: 1 } } as const)

All 7 comments

This can already be achieved with a little bit of boilerplate.

interface State extends Readonly<{
    prop1: string;
    prop2: string;
    ...
    prop22: string;
}> { }

http://www.typescriptlang.org/play/#src=%0D%0Ainterface%20Foo%20extends%20Readonly%3C%7B%0D%0A%20%20%20%20prop1%3A%20string%3B%0D%0A%20%20%20%20prop2%3A%20number%3B%0D%0A%7D%3E%20%7B%20%7D%0D%0A%0D%0Adeclare%20const%20f%3A%20Foo%3B%0D%0A%0D%0Af.prop1%20%3D%20'test'%3B%0D%0A

Have realised that my suggestion above has some limitations. Like using this for self-referential types

interface Foo extends Readonly<{
    prop1: string;
    prop2: number;
    prop3: this['prop1']; // 'this' type is available only in a non-static member of a class or interface.
}> { }

const interface might be nice for deep readonly (kinda like { a: { b: 1 } } as const)

I think there might be value in extending this to cover a C++ like const behavior...

const interface Foo {
  property: string; // Implicitly readonly
  method(): void; // Cannot mutate `this`
}

interface Bar {
  const method(): void; // Guaranteed non-mutating
  mutatingMethod(): void; // Could mutate `this`
}

function baz(obj: const Bar) { // Require "obj" to not be mutated by implementation
  obj.mutatingMethod(); // đź’Ą illegal call of mutating method on const reference
}

I don't have particularly strong feelings on the const versus readonly term here... const is "nice" to me as it relates nicely to what this means in C++, which (some) people are used to.

There's a lot of discussion about various ways to apply readonly to individual properties, but nothing about readonly objects themselves. I've found that TypeScript's ReadonlyArray type is very trustworthy in preventing accidentally passing an array that shouldn't be mutated somewhere that could possibly mutate it:

const FOO = [1, 2] as const; // "TS has my back - this should never change"
function use(arr: Array<number>) { arr[0]++; }
use(FOO); // error!

But readonly properties on objects just don't have sufficient checking, nor are there any guards against passing objects to contexts that might mutate the object structure itself:

const FOO = {x: 1} as const; // "But is it really const?"
function use(obj: {x: number, y?: number}) { obj.x++; obj.y = 2; }
use(FOO); // "This is fine"

Playground Example

Ideally a readonly {x: number} would not be allowed to be silently upcast to {x: number}, possibly not even to {readonly x: number} since the readonly modifier outside would provide additional guarantees about the readonly-ness of the object structure itself. I don't have the slightest idea how feasible this actually is.

@shicks

Ideally a readonly {x: number} would not be allowed to be silently upcast to {x: number},

~This is already how readonly works, it isn’t silently upcast.~

~The reason there is no error is your example is because {x:1} as const produces the type {x:1} rather than {readonly x:1}.~
~The const modifier is only narrowing number to 1.~

EDIT: I am wrong. It is being upcast. Viewing the playground on my phone it wasn’t showing the readonly modifier

This seems like sufficient scope for its own feature request, no?

Was this page helpful?
0 / 5 - 0 ratings