-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion for readonly interface #21152
Comments
This can already be achieved with a little bit of boilerplate. interface State extends Readonly<{
prop1: string;
prop2: string;
...
prop22: string;
}> { } |
Have realised that my suggestion above has some limitations. Like using 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.
}> { } |
|
I think there might be value in extending this to cover a C++ like 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 |
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 |
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? |
Love this proposal. I suspect it wouldn't be that big of a challenge to implement and the value is immense. At a project I'm currently working on, as a team we settled on @acutmore's suggestion, but using DeepReadonly: declare type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
interface Interface extends DeepReadonly<{
data: {
prop1?: string; // this would be mutable if we extended for Readonly instead of DeepReadonly
};
}> {} This is still quite a bit of boilerplate, and the empty curly braces at the end ( This would be so more readable: readonly interface State {
prop1: string;
prop2: string;
...
prop22: string;
} One question does arise: should the Intuitively I think a deep/recursive readonly makes the most sense. |
I guess my point is that we don't actually gain that much by simply distributing EDIT: fixed typo |
Ah, I see what you mean @shicks! I hadn't seen your playground example before. That is really bad :/ I agree with you. I think they should be separate things though. I'd intuitively expect |
@shicks is there a GH issue to specifically tackle the downcasting? |
I'm not aware of one. |
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:
equals to:
The text was updated successfully, but these errors were encountered: