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
{{ message }}
Repository navigation
add enforceReadonlyAssignability flag for smoother transition to true readonly modifiers #13002
this can be seen in the nightly build as of Dec 17 (as well as at playground)
interfaceReadOnlyData{readonlyvalue: number;}interfaceWritableData{value: number;}functionerase(data: WritableData): void{data.value=0;}constreadOnly: ReadOnlyData={value: 1};erase(readOnly);// expected a type error, actual: no problem
this can't be serious, can it? why is it called readonly?
please note there are no type assertions or any other attempts to trick the type system, it just simply doesn't work
Aleksey-Bykovreadonly basically means you may not modify it. It guarantees no actual immutability of the binding (heck, even Scala doesn't provide facilities that deep - val name = value and def name = value carry the same signature, and toString is a property). Consider it a case of "we're all consenting adults here", not "you shall not modify this value ever" (the latter is what Object.freeze is for).
@iashmeadows next time you are buying a car and you read "a sport BMW coupe", you say sweet, just what I wanted, you write a check, get your car and drive it home, next day you are all excited to show it to your friends, you stop at a store to get a 6 pack on the way there, you open the trunk and put your beer in it, when you close it you BMW turns into Honda Odyssey... you are like what? you are all pissed and stuff, you take the beer out and it turns into BMW again, you are like what!!! you call the dealer who sold it to you, they drop on you first, it's normal, then they pick up and say you know it's accoring to issue #6532 where some housewife complained that the trunk of BMW isn't big enough for shopping at Costco and taking kids to the beach, and that's exactly why we made a hard decision to turn iT into Honda Odyssey every time anything is put in its trunk, you are like wtf!!! So you go there and you are like: nothing was said about this shit anywhere! And what you hear back is: well we never said we sell true BMW''s why are you surprised? And people around who saw it are like: hey let's be adults here. F**k what?
Reacted by Aluan Haddad, Bartosz Gościński, Maxim Kulikov, Maksim Sinik, Timur Seitosmanov, Marin Marinov and scottmohekey
Reacted by Alex and Agamnentzar
Reacted by Daniel Rosenwasser, Claudia Meadows, normalser, Kagami Sascha Rosylight, Aluan Haddad, Tingan Ho, Yegor Roganov, Herrington Darkholme, Frederick Zhang, Eyal and 15 more
Aleksey-Bykov In case you missed it, the "we're all consenting adults here" was a reference to a common saying in the Python ecosystem, regarding member privacy, types, etc. 😉
(Basically, conventions are usually sufficient to imply "you're on your own" for diving into internals, and there's only so much you can do to prevent them from doing it, especially without the use of closures.)
In case you missed it, the "we're all consenting >adults here" was a reference to a common >saying in the Python ecosystem, regarding >member privacy, types, etc. 😉
Python gets a lot of things wrong. I never understood the appeal and popularity of that language. Irregardless that doesn't make it a good basis for anything.
It guarantees no actual immutability of the >binding (heck, even Scala doesn't provide >facilities that deep - val name = value and def >name = value carry the same signature, and >toString is a property).
I don't see the relevance, val and def both mean the reference, to a property method or variable, may not be reassigned. They differ in eagerness vs laziness and some other characteristics, type inference characteristics for example, but they don't have different mutability. With respect to deep immutability, which is not what is asked for here, Scala does allow it, you just have to use the right modifier, valordef, all the way down and not var. Scala does enforce this at the type system level.
Regardless, Scala has a nominal type system with support for optional explicit structural subtyping. Furthermore it doesn't permit these types of readonly violations. I don't think it is the right analogy.
I'm not saying TypeScript made a bad decision here, but personally I think it would have been great if readonly had resulted in code emit using defineProperty({ writable: false, ... }), at least it would have made classes useful. But that's a whole other argument.
Reacted by Tingan Ho, Eyal, Anton Trofymenko, Dmitry Starosta, Asad Saeeduddin and Junyoung/"Clare" Jang
Type driven emit would be nice in many scenarios, but even if we never get that, a type error here would be useful. Do readonly properties only get checked for reassignment within class members right now?
Re: comments above, the issue here is not to do with emit, or the fact that readonly doesn't mean const. It's just the looseness of type checking between the interfaces with mismatched properties. If a property could be explicitly marked mutable, it would not be satisfied by a readonly property and the conversion disallowed.
That would be the ideal if the readonly modifier had existed forever. The problem is backward compatibility. To quote Anders Hejlsberg (@ahejlsberg) in the comment linked to above:
Specifically, we can't interpret the absence of a readonly modifier to mean read-write, we can only say that we don't know. So, if an interface differs from another interface only in the readonly modifiers on its properties, we have to say that the two interfaces are compatible. Anything else would be a massive breaking change.
So now we have a trichotomy:
{ readonly x: number } - x is definitely not writable { mutable x: number } - x is definitely writable { x: number } - x may or may not be writable - coder has not specified
If mutable was added to the language then your:
interfaceWritableData{mutablevalue: number;}
would no longer be compatible with ReadableData. It would allow us to specify definite mutability wherever we needed that safety.
I think of this as similar to the bivariance compromise in TS today. Any fix to it in the future will allow us to improve the situation where we ask for it, but the default will have to continue to be bivariance, to avoid breaking so much existing user code.
(OTOH as TS 2.0 was a major version bump, maybe a massive breaking change would be acceptable to some, but TS is more cautious than that, which is a good thing IMO).
Aleksey-Bykov Indeed a mutable modifier is effectively meaningless. Everything is mutable unless otherwise indicated.
Daniel Earwicker (@danielearwicker) specifically for that reason there should not be new syntax because the old syntax will have to mean what the new syntax means implicitly and indefinitely. All this will do is create cognitive load
Specifically, we can't interpret the absence of a readonly modifier to mean read-write, we can only say that we don't know. So, if an interface differs from another interface only in the readonly modifiers on its properties, we have to say that the two interfaces are compatible. Anything else would be a massive breaking change.
It could be behind a flag : usually it's a convenient way to handle these kind of breaking changes.
because the old syntax will have to mean what the new syntax means implicitly and indefinitely.
But that isn't what the old syntax meant. { name: string; } didn't mean that name was definitely mutable, and so it cannot be changed to mean that now. The world is full of TS 1.x code that uses such declarations for things that should not (or cannot) be modified because that code was written before TS 2. You can't change the meaning of all that code now.
All widely used languages are products of evolution and contain some scars of that process. Only academic niche languages that no one uses are totally spotless.
Look at it this way: suppose functional programming takes over the world and everyone's properties are all now readonly. Now it looks like readonly should have been the default, to remove all the noise of everything being marked readonly. But that change would break most existing apps utterly.
Arnaud Benhamdine (@abenhamdine) - yes though I expect there would be implications for maintaining type definition files so they can be used either way. If we have readonly and mutable then all type definitions must explicitly say which their properties are, so they are not changed by the compiler switch.
{ x: number } - x may or may not be writable - coder has not specified
you cracked me up :) so when you see { x: number } and you need to assign x it basically means the end of the work day for you because you can't make a decision based on how you defined it
An off by default, option in the vein of --enforceReadonlyAssignability, would be a very nice thing to have. It is of course a lot of time and effort, and I do not take that lightly, but it would be very beneficial. It seems as relevant, but of course less broadly valuable, as --strictNullChecks from a code correctness point of view.
Reacted by Anton Trofymenko, Timm Preetz, Jared Russell, SlurpTheo, Junyoung/"Clare" Jang, Daniel M. and Leo Friedrichs
To be honest I miss the old title. It was so much more memorable. These days, when I get frustrated with readonly and subsequently try to search for this issue, I actually have hard time finding it.
Reacted by Anton Trofymenko, Timur Seitosmanov and Alexandre Galays
Reacted by Aluan Haddad, Alexandre Galays and SlurpTheo
Activity
RyanCavanaugh commentedon Dec 17, 2016
This was described at #6532 (comment)
dead-claudia commentedon Dec 18, 2016
Aleksey-Bykov
readonlybasically means you may not modify it. It guarantees no actual immutability of the binding (heck, even Scala doesn't provide facilities that deep -val name = valueanddef name = valuecarry the same signature, andtoStringis a property). Consider it a case of "we're all consenting adults here", not "you shall not modify this value ever" (the latter is whatObject.freezeis for).zpdDG4gta8XKpMCd commentedon Dec 18, 2016
@iashmeadows next time you are buying a car and you read "a sport BMW coupe", you say sweet, just what I wanted, you write a check, get your car and drive it home, next day you are all excited to show it to your friends, you stop at a store to get a 6 pack on the way there, you open the trunk and put your beer in it, when you close it you BMW turns into Honda Odyssey... you are like what? you are all pissed and stuff, you take the beer out and it turns into BMW again, you are like what!!! you call the dealer who sold it to you, they drop on you first, it's normal, then they pick up and say you know it's accoring to issue #6532 where some housewife complained that the trunk of BMW isn't big enough for shopping at Costco and taking kids to the beach, and that's exactly why we made a hard decision to turn iT into Honda Odyssey every time anything is put in its trunk, you are like wtf!!! So you go there and you are like: nothing was said about this shit anywhere! And what you hear back is: well we never said we sell true BMW''s why are you surprised? And people around who saw it are like: hey let's be adults here. F**k what?
DanielRosenwasser commentedon Dec 18, 2016
Regardless of whether or not I agree on that perspective of
readonly, I have to admit I enjoyed the analogy. 😄dead-claudia commentedon Dec 18, 2016
Aleksey-Bykov In case you missed it, the "we're all consenting adults here" was a reference to a common saying in the Python ecosystem, regarding member privacy, types, etc. 😉
(Basically, conventions are usually sufficient to imply "you're on your own" for diving into internals, and there's only so much you can do to prevent them from doing it, especially without the use of closures.)
aluanhaddad commentedon Dec 18, 2016
@isiahmeadows
Python gets a lot of things wrong. I never understood the appeal and popularity of that language. Irregardless that doesn't make it a good basis for anything.
I don't see the relevance,
valanddefboth mean the reference, to a property method or variable, may not be reassigned. They differ in eagerness vs laziness and some other characteristics, type inference characteristics for example, but they don't have different mutability. With respect to deep immutability, which is not what is asked for here, Scala does allow it, you just have to use the right modifier,valordef, all the way down and not var. Scala does enforce this at the type system level.Regardless, Scala has a nominal type system with support for optional explicit structural subtyping. Furthermore it doesn't permit these types of readonly violations. I don't think it is the right analogy.
I'm not saying TypeScript made a bad decision here, but personally I think it would have been great if
readonlyhad resulted in code emit usingdefineProperty({ writable: false, ... }), at least it would have made classes useful. But that's a whole other argument.masaeedu commentedon Dec 19, 2016
Type driven emit would be nice in many scenarios, but even if we never get that, a type error here would be useful. Do
readonlyproperties only get checked for reassignment within class members right now?danielearwicker commentedon Jan 8, 2017
Re: comments above, the issue here is not to do with emit, or the fact that readonly doesn't mean const. It's just the looseness of type checking between the interfaces with mismatched properties. If a property could be explicitly marked
mutable, it would not be satisfied by areadonlyproperty and the conversion disallowed.zpdDG4gta8XKpMCd commentedon Jan 9, 2017
here is a dichotomy: a property is either readonly or writable, there is no other outcome
when we say
{ x: number }we mean writablexwhen we say
{ readonly x: number }we meanxis only for readingwhat else do we need? what is the third state implied by
mutablemdanielearwicker commentedon Jan 9, 2017
That would be the ideal if the
readonlymodifier had existed forever. The problem is backward compatibility. To quote Anders Hejlsberg (@ahejlsberg) in the comment linked to above:So now we have a trichotomy:
{ readonly x: number }-xis definitely not writable{ mutable x: number }-xis definitely writable{ x: number }-xmay or may not be writable - coder has not specifiedIf
mutablewas added to the language then your:would no longer be compatible with
ReadableData. It would allow us to specify definite mutability wherever we needed that safety.I think of this as similar to the bivariance compromise in TS today. Any fix to it in the future will allow us to improve the situation where we ask for it, but the default will have to continue to be bivariance, to avoid breaking so much existing user code.
(OTOH as TS 2.0 was a major version bump, maybe a massive breaking change would be acceptable to some, but TS is more cautious than that, which is a good thing IMO).
aluanhaddad commentedon Jan 9, 2017
Aleksey-Bykov Indeed a
mutablemodifier is effectively meaningless. Everything is mutable unless otherwise indicated.Daniel Earwicker (@danielearwicker) specifically for that reason there should not be new syntax because the old syntax will have to mean what the new syntax means implicitly and indefinitely. All this will do is create cognitive load
abenhamdine commentedon Jan 9, 2017
It could be behind a flag : usually it's a convenient way to handle these kind of breaking changes.
danielearwicker commentedon Jan 9, 2017
Aluan Haddad (@aluanhaddad)
But that isn't what the old syntax meant.
{ name: string; }didn't mean thatnamewas definitely mutable, and so it cannot be changed to mean that now. The world is full of TS 1.x code that uses such declarations for things that should not (or cannot) be modified because that code was written before TS 2. You can't change the meaning of all that code now.All widely used languages are products of evolution and contain some scars of that process. Only academic niche languages that no one uses are totally spotless.
Look at it this way: suppose functional programming takes over the world and everyone's properties are all now
readonly. Now it looks likereadonlyshould have been the default, to remove all the noise of everything being markedreadonly. But that change would break most existing apps utterly.danielearwicker commentedon Jan 9, 2017
Arnaud Benhamdine (@abenhamdine) - yes though I expect there would be implications for maintaining type definition files so they can be used either way. If we have
readonlyandmutablethen all type definitions must explicitly say which their properties are, so they are not changed by the compiler switch.So even then, we'd still need
mutable.6 remaining items
zpdDG4gta8XKpMCd commentedon Jan 9, 2017
you cracked me up :) so when you see
{ x: number }and you need to assignxit basically means the end of the work day for you because you can't make a decision based on how you defined itzpdDG4gta8XKpMCd commentedon Jan 9, 2017
Daniel Earwicker (@danielearwicker) Asad Saeeduddin (@masaeedu)
let me share a secret:
--noLiballows you to ignore new type definitions and use the old onesreadonlyproblem solved
danielearwicker commentedon Jan 9, 2017
Cool, issue closed then!
aluanhaddad commentedon Jan 9, 2017
Ryan Cavanaugh (@RyanCavanaugh) I suppose not.
An off by default, option in the vein of
--enforceReadonlyAssignability, would be a very nice thing to have. It is of course a lot of time and effort, and I do not take that lightly, but it would be very beneficial. It seems as relevant, but of course less broadly valuable, as--strictNullChecksfrom a code correctness point of view.zpdDG4gta8XKpMCd commentedon Jan 9, 2017
in support of the previous speaker, the topic was renamed
RyanCavanaugh commentedon Jan 9, 2017
Tracking this at #13347
gcnew commentedon Jul 19, 2017
To be honest I miss the old title. It was so much more memorable. These days, when I get frustrated with
readonlyand subsequently try to search for this issue, I actually have hard time finding it.