You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
Have realised that my suggestion above has some limitations. Like using this for self-referential types
interfaceFooextendsReadonly<{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 behavior...
constinterfaceFoo{property: string;// Implicitly readonlymethod(): void;// Cannot mutate `this`}interfaceBar{constmethod(): void;// Guaranteed non-mutatingmutatingMethod(): void;// Could mutate `this`}functionbaz(obj: constBar){// Require "obj" to not be mutated by implementationobj.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:
constFOO=[1,2]asconst;// "TS has my back - this should never change"functionuse(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:
constFOO={x: 1}asconst;// "But is it really const?"functionuse(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.
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
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 Ashley Claymore (@acutmore)'s suggestion, but using DeepReadonly:
declaretypeDeepReadonly<T>={readonly[KinkeyofT]: DeepReadonly<T[K]>};interfaceInterfaceextendsDeepReadonly<{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 (}> { }) aren't very elegant or intuitive.
I guess my point is that we don't actually gain that much by simply distributing readonly to all the fields when the implicit downcasting (I believe I misspoke earlier when I called it upcasting) is on by default. Introducing new syntax provides an opportunity to also introduce stricter defaults that would otherwise be infeasible: if the readonly interface refused to implicitly downcast, I argue it would be much safer and more useful (and would be more likely to pull its own weight) than if it were simply syntactic sugar around something that's already possible.
Ah, I see what you mean Stephen Hicks (@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 readonly to be syntax sugar (pretty much), but it's also a good opportunity to fix a mistake from the past.
Activity
acutmore commentedon Jan 12, 2018
This can already be achieved with a little bit of boilerplate.
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
acutmore commentedon Jan 12, 2018
Have realised that my suggestion above has some limitations. Like using
thisfor self-referential typesaleclarson commentedon Mar 8, 2019
const interfacemight be nice for deep readonly (kinda like{ a: { b: 1 } } as const)RomainMuller commentedon Mar 13, 2019
I think there might be value in extending this to cover a C++ like
constbehavior...I don't have particularly strong feelings on the
constversusreadonlyterm here...constis "nice" to me as it relates nicely to what this means in C++, which (some) people are used to.shicks commentedon Dec 14, 2019
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:
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:
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 thereadonlymodifier 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.acutmore commentedon Dec 16, 2019
Stephen Hicks (@shicks)
This is already howreadonlyworks, it isn’t silently upcast.The reason there is no error is your example is because{x:1} as constproduces the type{x:1}rather than{readonly x:1}.The const modifier is only narrowingnumberto1.EDIT: I am wrong. It is being upcast. Viewing the playground on my phone it wasn’t showing the readonly modifier
shicks commentedon Dec 16, 2019
This seems like sufficient scope for its own feature request, no?
lautarodragan commentedon May 26, 2021
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 Ashley Claymore (@acutmore)'s suggestion, but using DeepReadonly:
This is still quite a bit of boilerplate, and the empty curly braces at the end (
}> { }) aren't very elegant or intuitive.This would be so more readable:
One question does arise: should the
readonlymodifier for the interface be a deep-readonly, or a shallow one?Intuitively I think a deep/recursive readonly makes the most sense.
shicks commentedon May 28, 2021
I guess my point is that we don't actually gain that much by simply distributing
readonlyto all the fields when the implicit downcasting (I believe I misspoke earlier when I called it upcasting) is on by default. Introducing new syntax provides an opportunity to also introduce stricter defaults that would otherwise be infeasible: if thereadonly interfacerefused to implicitly downcast, I argue it would be much safer and more useful (and would be more likely to pull its own weight) than if it were simply syntactic sugar around something that's already possible.EDIT: fixed typo
lautarodragan commentedon Jul 8, 2021
Ah, I see what you mean Stephen Hicks (@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
readonlyto be syntax sugar (pretty much), but it's also a good opportunity to fix a mistake from the past.lautarodragan commentedon Jul 15, 2021
Stephen Hicks (@shicks) is there a GH issue to specifically tackle the downcasting?
shicks commentedon Jul 15, 2021
I'm not aware of one.
acutmore commentedon Jul 15, 2021
#13347
RyanCavanaugh commentedon Oct 20, 2025
Les track this at #45535
typescript-bot commentedon Oct 23, 2025
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.