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;
}
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"
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?
Most helpful comment
const interfacemight be nice for deep readonly (kinda like{ a: { b: 1 } } as const)