Closed
Labels
CommittedThe team has roadmapped this issueThe team has roadmapped this issueFixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Milestone
Description
Some properties in JavaScript are actually read-only, i.e. writes to them either fail silently or cause an exception. These should be modelable in TypeScript.
Previous attempts to design this have run into problems. A brief exploration:
interface Point {
x: number;
y: number;
}
interface ImmutablePoint {
readonly x: number;
readonly y: number;
}
var pt: ImmutablePoint = { x: 4, y: 5 }; // OK, can convert mutable to non-mutable
pt.x = 5; // Error, 'pt.x' is not a valid target of assignment
var pt2: Point = pt; // Error, cannot convert readonly 'x' to mutable 'x'
// Possibly bad behavior
var pt3: Point = { x: 1, y: 1 };
var pt4: ImmutablePoint = pt3; // OK
pt3.x = 5; // pt4.x is also changed?
// Really bad behavior
/** This function was written in TypeScript 1.0 **/
function magnitudeSquared(v: { x: number; y: number }) {
return v.x * v.x + v.y * v.y;
}
// Now try to use it with ImmutablePoint
console.log(magnitudeSquared(pt)); // Error, cannot use readonly object in non-readonly callPossible solutions?
- Allow bivariance of mutability: this is very unsound
- Something else clever? C++ did not do well with const contamination
Metadata
Metadata
Assignees
Labels
CommittedThe team has roadmapped this issueThe team has roadmapped this issueFixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
everson commentedon Jul 21, 2014
I think this is probably the most important feature in terms of making Typescript a safer language. E.g. You are guaranteed that the object will not be mutated along the way and that simplifies reasoning a lot.
however, I don't see the problem with magnitudeSquared, it does not change 'v', so, it should work fine with both read-only and mutable objects.
basarat commentedon Jul 30, 2014
👍
RyanCavanaugh commentedon Jul 30, 2014
The problem is, you don't know this without reading the function implementation, and you might not have access to the implementation. Let's say someone gives you their library with a 1.0 TypeScript definition file:
One of these functions modifies the object, one of them doesn't. You can't tell by looking at their signatures -- they're identical.
Now we have two obvious options
Assume all functions arguments are actually treated as read-only?
That fails to catch this problem
Worse, now you have to opt in to non-readonliness. When you upgrade your definition file to have the read/write information, now you're going to have to write
mutablebasically everywhere:Assume all function arguments are actually mutable?
Now I can't use
magnitudeSquaredwithout a cast:And you get to write
readonlyeverywhere:joewood commentedon Nov 4, 2014
Can this be re-opened as a request? Given the increased use of functional programming with JavaScript and immutable data structures (see React and Flux). I think this would be great as an opt-in feature, maybe with a top-level decorator enabling it to avoid the issues of default behavior (i.e. ignore the check by default).
RyanCavanaugh commentedon Nov 4, 2014
I'm not sure what you're asking? The issue is currently open.
joewood commentedon Nov 4, 2014
Sorry, misread the status.
Jxck commentedon Nov 19, 2014
+1 for readonly keyword support.
IanYates commentedon Nov 19, 2014
+1 for something along these lines. The problems described by Ryan Cavanaugh (@RyanCavanaugh) are quite real but the suggestion by Joe Wood (@joewood) to have some sort of decorator seems to be a smart one. I guess it might be along the lines of 'use strict' at the start of functions but probably more explicit, like
mutable classvsimmutable classand
mutable modulevsimmutable moduleto specify the defaults within a block of code at the module or class level. For back-compat reasons, mutability would have to be assumed, right?
Perhaps along the same lines, where ? is used on a parameter to indicate it's optional, could other annotations be added, along with one indicating either (like how
anythrows away types)?RandScullard commentedon Nov 19, 2014
"The perfect is the enemy of the good." As TypeScript stands today, the compiler won't even tell me when I set a property with no setter -- with the full implementation available. This has been raised as an issue multiple times, and those other issues all point back to this one, so it seems pretty important that progress be made in this area. To me,
having the readonly decorator andcatching the assignment-with-no-setter error would be a huge step in the right direction, even if it doesn't solve the third-party library problem.Edit: Removed reference to readonly decorator. Readonly is a tar pit, and my point is around get/set, not interfaces.
danpaul88 commentedon Nov 25, 2014
+1 for a readonly modifier or similar. The scenario I am frequently running into is wanting to define an interface with properties that only requires a getter, which I would then expect the compiler to enforce by generating an error if the code tries to set a readonly property. Implementations would be free to provide a setter if they choose to do so, but it should only be usable in code if the object is being referenced as the implementing class or a derivative of it.
My personal preference would be the way c# does it, allowing you to specify get and/or set in the interface, but the readonly case is by far the most useful (other than the full get/set case).
Eyas commentedon Nov 27, 2014
Thinking out loud here:
While
readonlyfields are important, perhaps an easier thing to implement, which doesn't have some of the problems Ryan Cavanaugh (@RyanCavanaugh) outlined isimmutableclasses/interfaces.Suppose you have:
Given the properties of an
imutableobject, we can change how assignment behaves, to instead clone and freeze in JS.So that:
The
pt4assignment would basically look like:The compiler can then optimize the
clone & freezeinto only afreezeif:1- we are using a literal assignment
2- possibly, if the variable/value we are copying is dead after this line (when dealing with global scope ,etc.)
Some problems remain with this:
magnitudeSquaredproblem, we'd need to declare it asmagnitudeSquared(v: immutable v { x: number, y: number }).joewood commentedon Nov 29, 2014
I was actually thinking of something more like the
constmodifier in C++. This would be applied to the object instance, providing an immutable view of an existing object. So something more like:This way it's an opt-in on the interface, and can be passed down to child objects through accessor functions (or getters) that are declared
const. Declaring a functionconstessentially applies the modifier tothis(again, in the same way as C++ works).So, in practical terms for libraries like react.js, the
setStatefunction would be mutable but the accessor functions tostateandpropswould be immutable.metaweta commentedon Dec 13, 2014
I prefer Eyas' suggestion. It's clean and simple. JavaScript has so many ways to modify state: adding/removing/deleting properties, side-effecting getters, proxies, etc. that introducing a concept like const will necessarily be horribly complicated.
There's also the issue of claiming that a method or argument in a JS library is const when it turns out not to be; there's no way to check it and not even runtime errors will be generated unless the compiler wraps arguments in a read-only proxy.
112 remaining items
mhegazy commentedon May 20, 2017
And readonly x: number is not good enough?
mhegazy commentedon May 20, 2017
Or u r looking for public get private set?
NN--- commentedon May 20, 2017
It is debatable and as I said I didn't find what is considered to be a good practice.
The main concern is that readonly becomes 'read-write' in the output JS and it can be changed by some bad code.
Joel Day (@joelday) Suggests to use get-only properties in the class though.
Mohamed Hegazy (@mhegazy) Yes, but be more specific , constructor only set.
kitsonk commentedon May 20, 2017
Terse get syntax like C# was discussed in #10911.
Since properties (at the moment) aren't ES spec, it could be argued though that the
readonlymodified in a class could make a non-writable value descriptor. If someone wanted to truly shadow the value, a getter only would be the other way. So this:Would de-sugar to:
mhegazy commentedon May 20, 2017
Why is readonly diffrent from private? Privacy is not maintained in the generated js either.
NN--- commentedon May 20, 2017
Mohamed Hegazy (@mhegazy) You are correct. I mistakenly thought that private members are implemented in some different way.
Then it makes sense for same way readonly implementation.
I guess it is offtopic now. It needs some other syntax for truly readonly properties to be implemented by defineProperty and privates via symbol or some other nice hack.
Thanks.
paleo commentedon Sep 25, 2017
The usage of
readonlyis not completely clear for me. I wrote a question on Stackoverflow.This following code compiles, but is it semantically correct?
The member
valueis declared asreadonlybut its value is changing.I would like to know if this use of
readonlyon the membervalueis a good way to proceed. The syntax is OK. But is this example semantically correct in TypeScript? Is that what the modifierreadonlyis for?NN--- commentedon Sep 25, 2017
Thomas Mur (@paleo) readonly is similar to const, it doesn't define deep immutability, just some way to make less mistakes.
For true immutability, you need to write code which makes types immutable.
raveclassic commentedon Sep 26, 2017
Thomas Mur (@paleo)
readonlydoesn't guarantee that a value won't change in time, it just doesn't let you write to it. That's why the word isreadonly, notconstant.zpdDG4gta8XKpMCd commentedon Sep 26, 2017
that's not true either: #13002
raveclassic commentedon Sep 26, 2017
Aleksey-Bykov Previous issue title was a bit closer then...
paleo commentedon Sep 26, 2017
I quote Anders Hejlsberg (@ahejlsberg) from his PR:
In my question on StackOverflow, I wrote two use cases using
readonly. The first case matches well to "cannot be modified by me". But the second case, that I have reported above, would rather be a "cannot be modified by you".Is that what the modifier
readonlyis for? Is it valid from a semantic point of view?kitsonk commentedon Sep 26, 2017
Like a lot of the TypeScript typing system, it is not enforceable at run-time, it is a type contract.
constis a ECMAScript run-time concept which TypeScript can fully model the run-time behaviour.readonlycorrectly aligns to run-time behaviour when the property in question has a getter only or is a value property that is not writable. At other times, it is a design time only construct to improve type safety (just asstringornumberare design time only constructs).paleo commentedon Sep 27, 2017
Thank you, it's clearer.
I realize that I already had the answer in 2016, from Ryan Cavanaugh (@RyanCavanaugh) . At that time the ES5 getter solution was not available to me because my code had to remain ES3 compatible, so I left angry and didn't take the time to understand the information. He said:
This is the final answer: yes, the
readonlymodifier can be used with an ES5 getter to achieve this semantics.