Theyād work by themselves, theyāll be an actual defined type to the compiler. Itās just that (in this proposal as of now) referring to them by typename canāt be done without using escaped identifiers, or by using the @as
annotation. Your example above would work just fine because itād be inferred to be the persist type.
Iāve had the need for this quite often when writing bindings!
This rfc will make rescript more easily to use!
This would be great!
A PoC of this that soon will be usable (hopefully) exists here: PoC of nested record definitions by zth Ā· Pull Request #7241 Ā· rescript-lang/rescript Ā· GitHub
Real types, with autogenerated names that arenāt possible to reference unless using escaped identifiers (a reminder that you should probably break it out to a real type definition if you want to reuse it).
One could also imagining supporting the attribute to set the name for a generated type:@as
I think itās right that reuse is explicit. What case does the @as
addition proposal needed specifically?
It reminds me of the exportFragmentSpreadSubTypes
option in the GraphQL code generator. It was quickly abused at scale, so it had to be disabled.
Yeah I lean towards that as well - make it a concrete definition if you need reuse. We can make the tooling make it super easy to extract to a concrete definition.
Would nested variants be a stretch or do you want to keep this scoped to records?
Definitely not ruling out that, but we should start with records and see how thatās used. Variants are slightly different because of things like @unboxed
and @tag
where you often want/need to configure the variant at the type definition level. At a glance I think itās a bit unclear how to achieve that in an intuitive way inline without introducing new syntax. But definitely not out of the question.
Iām a little bit skeptic about using the @as
attribute for renaming the underline types.
Because currently it exists to adjust the runtime representation of ReScript data and simply putting the @as
to a wrong place will result in a completely different result:
type options = {
startFrom: float,
@as("persistOptions") persist?: { // makes the type `type persistOptions` instead of `type \"options.persist\"
fileName: string,
path?: string,
fileConfig?: {
extension?: string
}
}
}
Iād like to keep @as
only for changing the runtime representation.
Would it be possible to have indexed access types to alias nested types directly? They are well proved on typescript nested record access
type options = {
startFrom: float,
persist?: {
fileName: string,
path?: string,
fileConfig?: {
extension?: string
}
}
}
// alias for \"options.persist\"
type persistAlias = options["persist"]
// alias for \"options.persist.fileConfig\"
type fileConfigAlias = options["persist"]["fileConfig"]
This feature and local type are super useful in Typescript.
Unfortunately I donāt think that functionality from TypeScript translates well to ReScript. The simple cases might (just the equivalent of dot access), but add on things like variants, tuples and more and weād need likely need to invent more syntax, and do a pretty complicated lookup of types. This lookup would also be alot less useful than in TS. As an example, what if you have a variant where 2 cases has the same prop name, but the props have different types? In TS itād become typeOfA | typeOfB
because thatās how the TS type system works. But in ReScript you canāt mix types like that.
Then thereās the added question around whether to keep things as explicit as possible, which would favor not having this type of lookup. This is also the reason why weāve rejected @as
.
Personally, that functionality in TS has almost always caused more issues than it has solved in codebases Iāve worked in.
Could be explored for sure if someone is interested in taking a stab at investigating how a complete ReScript version of that feature could look/work. But, just like when considering @as
, I think we need to keep this type of thing simple to start out.
Iāve been sidetracked with other things, but I look forward to continuing work on this feature soon! Hoping we can land it in v12, thatād be great. Now that I know we can do it I find myself missing this feature almost daily
At this point, what benefit is coming out other than just defining the types?
type fileConfig = {
extension?: string
}
type persist = {
fileName: string,
path?: string,
fileConfig?:
}
type options = {
startFrom: float,
persist?: persist
}
One of the things that draws me to using rescript at work is how straight forward the type system is even if it requires a little extra typing sometimes. Iāve seen some TS codebases that are a nightmare to understand due to getting creative with types
I think having the option is nice. imo this is easier to read through, as long as the inner types arenāt reused anywhere else.
type options = {
startFrom: float,
persist?: {
fileName: string,
path?: string,
fileConfig?: {
extension?: string
}
}
}
and it seems as though the team is not encouraging reusing types that are defined this way. It should be encouraged that if a type is reused, break it out
The proposal makes perfect sense when I understand records as immutable memory layouts that can no longer be split.
TypeScriptās structs have a quite different effect, since they allow declarations to be reinterpreted based on their structure. That seems pretty detrimental in a nominal language like ReScript.
I leave some additional concerns:
-
Polymorphic operations: Iāve argued for deprecating polymorphic operations we have since the introduction of universal operators in v12. For example, we support comparison operations between records. If we were to replace that with codegen based monomorphic operations, nested structures would add some complexity there.
-
Thereās actually a Record&Tuple proposal in progress in JS. Maybe we could experiment with it as an optional target once itās mature enough. So we need to check in advance that the proposal can interoperate with JS record semantics. I think itās probably a perfect match, but Iām not 100% sure yet.
Just want to be clear here that this version is āonlyā syntax sugar - actual record definitions are emitted for each nested record. Although syntax certainly would let us make it nested structures for real later on, itās not in scope (or evaluated/analyzed) for this first version.
This has now been merged and will be out as an experimental feature in v12