Repository navigation
Design Meeting Notes, 7/28/2017 #17502
Description
f# Readonly on Exports from Namespaces (#16720)
namespace N {
export class C { }
}
N.C = class {};- Namespaces have had this feature for a while now
- Breaking changes for something that's functioned that way this long seems strange.
- Declined
Readonly & Variance
-
One thing that comes up quite a bit is strict variance.
-
If we enabled stricter variance, that would imply that
Arraybecomes invariant.- To get a covariant array, you need a
ReadonlyArray. - This means everything gets two declarations -
TandReadonlyT
- To get a covariant array, you need a
-
Strict variance is easy to enable, but would be hard to make easy to use.
-
This leads us to start thinking about how to quickly make a readonly version of a type.
-
We've had an experimental type operator called
readonly- Much like
keyof T, you just writereadonly T.
- Much like
-
Like the
Readonlymapped type, it makes all propertiesreadonlyas well.- However, the
Readonly<T>mapped type doesn't eliminate mutating methods at all.
- However, the
-
This means we needed to add a way to indicate that a method doesn't mutate its object.
- Created the
this: readonlysyntax.
- Created the
-
Example:
class Foo { name: string; getName(this: readonly): string; setName(value: string): void; } // This: type ReadonlyFoo = readonly Foo; // ... is equivalent to this: type ReadonlyFoo = { readonly name: string; readonly getName(this: readonly): string; }
-
What about deep readonly?
- In other words, does
readonly Foo[]give you areadonly Array<readonly Foo>? - Have thus far decided no.
- Could create an
immutabletype operator- Q: Couldn't you implement that with mapped types?
- A: Potentially
- May be a bit confusing since state could be captured outside of current object
- Q: Couldn't you implement that with mapped types?
- Could create an
- In other words, does
-
In
--strictReadonlymode, thisreadonlyoperator would work as described above.readonly Tnot assignable toT.readonlyprops would not be assignable to mutable props.readonly Tremoves mutating methods
-
Outside of
strictReadonly,readonly Tis justReadonly<T> -
All of this means that library authors would need to enable readonly mode.
-
Would TypeScript be able to infer whether a method is readonly or not?
- Kind of tricky, methods transitively call each other.
-
How bad are strict variance modes without this?
- Special hell for passing callbacks.
- Need a
superconstraint for generics - otherwise many types stay invariant by virtue of taking aTas an input position.
-
Will people know how to use
this: readonly?- Seems like people will always need to be mindful of this when they write
.d.tsfiles. - How will we generate this for the DOM if the metadata isn't present? What about
readonly HTMLElement?
- Seems like people will always need to be mindful of this when they write
-
How do
this: readonlyparameters work with overloads?- Currently, if any overloads are not declared with
this: readonly, the property is eliminated. - That's not going to play well with libraries.
- Example: Knockout.
- Currently, if any overloads are not declared with
Activity
Igorbek commentedon Aug 1, 2017
Since there's no issues linked to 'Readonly & Variance' section (you may want to link #1394, #10717), I'll leave a few comments here.
I'm wondering why a connection mentioned between variance and readability in that way. My understanding is that a type may be co- or contravariant on some other type it uses. Like a function is covariant on its parameter argument types and contravariant on return type. In case of array, it may be covariant to type of its elements, but it does not imply that it must be read-only.
I claim that in some cases, it is not obvious or even impossible to be express variance with
readonly T. Consider:What would
readonly Transformer<I, O>mean? How is that useful for variance? More importantly is thatTransformeris covariant onIand contravariant onO.In #10717 I was proposing both co- and contra- varainces on generic types for both declaration and use sites:
DanielRosenwasser commentedon Aug 1, 2017
Igor Oleinikov (@Igorbek) I've been thinking more about use-site variance recently as well. Anders Hejlsberg (@ahejlsberg) will be able to give more direct context, but I'll try answering your questions.
In general, the connection is that the
readonlymodifier removes every single method that isn't marked asthis: readonly, which removes a great deal of methods that tend to make types invariant. While there's sometimes cases where generics in an input position don't necessarily modify contents of the containing type, this seems to be the case for a decent number of APIs. I believe the overall intent is that for many cases, thereadonlymodifier would make it significantly easier to manufacture a type that acts covariantly on its type parameters, which comes up quite a bit.As an example, look at how we've had to create
ReadonlyArrayas a restricted counterpart forArray. You'd presumably want aReadonlySetas well. Instead of declaring everything twice, we're exploring a more generalized mechanism.To be clear, we are still experimenting with these ideas. It's not even clear whether stricter variance is something we're committed to, but I'm glad you asked.
DanielRosenwasser commentedon Aug 1, 2017
Edit: I just noticed you pointed part of this out when you wrote:
By the way, I want to point out that for many containers, a
Foo<out T>is not necessarily read-only (imagine a method namedclear()).So neither
readonly-ing aFoo<T>nor passing an upper-bounded existential type argument (Foo<out T>) fully subsumes the other.Igorbek commentedon Aug 1, 2017
Thank you Daniel Rosenwasser (@DanielRosenwasser) for answering. That actually was my point that they, read-only-fity and covariance, are not always connected.
In my example, I just wanted to show something with current syntax. In future syntax it would be something like:
, or, in a more simplified case, I could just mark only
Iand let compiler do other work:(kinda random thoughts)
aluanhaddad commentedon Aug 1, 2017
Just 🚲:house:ing, but while I know TypeScript has been sticking to any established JavaScript precedents, wherever available, when adopting type level syntax (
extends/implements), C#'sinandoutreally are so much more expressive and easier to visually parse than<? super T>and<? extends T>, so if explicit variance annotations are ever added I think it might be nice to deviate and useinandoutas Igor Oleinikov (@Igorbek) does in his code examples above.mattmccutchen commentedon Sep 26, 2017
I propose that we continue the discussion of "Readonly & Variance" in #18770.