Skip to content

Commit e31365e

Browse files
authored
Add Core standard library support for genType (#6019)
* Add `Core` standard library support for `genType` See https://forum.rescript-lang.org/t/difficulty-with-shims-and-undefined-t-and-null-t/4240 * genType: treat Dict.t type as built-in. * Support `Promise.t`. * Support `Date.t`. * Support BigInt * Support RegExp. * Support Map and WeakMap * Support Set and WeakSet. * Update CHANGELOG.md * Clean up unused Record and RecordC. * genType: treat option<t> as t | undefined This produces strictly fewer conversion functions, as it does not need to transform `null` to `undefined`. * Remove special treatment of `{x:option<t>}` as optional field. This is a breaking changes, but aligns more closely with TS. The two TS types: ```ts type t1 = { x?: string }; type t2 = { x: (undefined | string) }; ``` now correspond to the ReScript types: ```res @genType type t1 = {x?: string} @genType type t2 = {x: Js.undefined<string>} ``` The special treatment of fields of option type comes from a time where records with optional fields did not exist. * Add examples of what can and cannot be used in bindings to functions. * Update CHANGELOG.md
1 parent 689b8fa commit e31365e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+480
-151
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ subset of the arguments, and return a curried type with the remaining ones https
2626
- Add support for toplevel `await` https://github.com/rescript-lang/rescript-compiler/pull/5940
2727
- Support optional named arguments without a final unit in uncurried functions https://github.com/rescript-lang/rescript-compiler/pull/5907
2828
- Add experimental suppport for directives. An annotation such as `@@directive("use client;")` emits `use client;` verbatim before imports https://github.com/rescript-lang/rescript-compiler/pull/5998
29+
- `genType`: add `Core` standard library support for the following builtin types: `Null.t`, `Nullable.t`, `Undefined.t`, `Dict.t<_>`, `Promise.t<_>`, `Date.t`, `BigInt.t`, `RegExp.t`, `Map.t<_, _>`, `WeakMap.t<_, _>`, `Set<_>`, `WeakSet<_>` https://github.com/rescript-lang/rescript-compiler/pull/6019
2930

3031
#### :boom: Breaking Change
3132

@@ -39,6 +40,9 @@ subset of the arguments, and return a curried type with the remaining ones https
3940
Also, `(. int) => string => bool` is not equivalen to `(. int, string) => bool` anymore.
4041
These are only breaking changes for unformatted code.
4142
- Exponentiation operator `**` is now right-associative. `2. ** 3. ** 2.` now compile to `Math.pow(2, Math.pow(3, 2))` and not anymore `Math.pow(Math.pow(2, 3), 2)`. Parentheses can be used to change precedence.
43+
- `genType`: streamline the treatment of optionals as undefined https://github.com/rescript-lang/rescript-compiler/pull/6022
44+
- Represent `option<t>` as `undefined | t` instead of `null | undefined | t`. This is more permissive when importing functions taking optional values (allows to use option types), but stricter when e.g. exporting ReScript functions taking arguments of option type. Fallback: use `Js.undefined<_>` instead.
45+
- Represent `{x:option<string>}` as `{x:(undefined | string)}` instead of `{x?: string}`. This is more in line with TS's behaviour. Fallback: use `{x?:string}`.
4246

4347
#### :bug: Bug Fix
4448

jscomp/gentype/Converter.ml

+11-85
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ type t =
55
| CircularC of string * t
66
| FunctionC of functionC
77
| IdentC
8-
| NullableC of t
98
| ObjectC of fieldsC
109
| OptionC of t
1110
| PromiseC of t
12-
| RecordC of fieldsC
1311
| TupleC of t list
1412
| VariantC of variantC
1513

@@ -68,8 +66,7 @@ let rec toString converter =
6866
|> String.concat ", ")
6967
^ " -> " ^ toString retConverter ^ ")"
7068
| IdentC -> "id"
71-
| NullableC c -> "nullable(" ^ toString c ^ ")"
72-
| ObjectC fieldsC | RecordC fieldsC ->
69+
| ObjectC fieldsC ->
7370
let dot =
7471
match converter with
7572
| ObjectC _ -> ". "
@@ -120,6 +117,7 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
120117
| Array (t, mutable_) ->
121118
let tConverter, tNormalized = t |> visit ~visited in
122119
(ArrayC tConverter, Array (tNormalized, mutable_))
120+
| Dict _ -> (IdentC, normalized_)
123121
| Function
124122
({argTypes; componentName; retType; typeVars; uncurried} as function_)
125123
->
@@ -189,10 +187,10 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
189187
else (IdentC, normalized_))
190188
| Null t ->
191189
let tConverter, tNormalized = t |> visit ~visited in
192-
(NullableC tConverter, Null tNormalized)
190+
(OptionC tConverter, Null tNormalized)
193191
| Nullable t ->
194192
let tConverter, tNormalized = t |> visit ~visited in
195-
(NullableC tConverter, Nullable tNormalized)
193+
(OptionC tConverter, Nullable tNormalized)
196194
| Object (closedFlag, fields) ->
197195
let fieldsConverted =
198196
fields
@@ -220,26 +218,6 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
220218
| Promise t ->
221219
let tConverter, tNormalized = t |> visit ~visited in
222220
(PromiseC tConverter, Promise tNormalized)
223-
| Record fields ->
224-
let fieldsConverted =
225-
fields
226-
|> List.map (fun ({type_} as field) -> (field, type_ |> visit ~visited))
227-
in
228-
( RecordC
229-
(fieldsConverted
230-
|> List.map (fun ({nameJS; nameRE; optional}, (converter, _)) ->
231-
{
232-
lblJS = nameJS;
233-
lblRE = nameRE;
234-
c =
235-
(match optional = Mandatory with
236-
| true -> converter
237-
| false -> OptionC converter);
238-
})),
239-
Record
240-
(fieldsConverted
241-
|> List.map (fun (field, (_, tNormalized)) ->
242-
{field with type_ = tNormalized})) )
243221
| Tuple innerTypes ->
244222
let innerConversions, normalizedList =
245223
innerTypes |> List.map (visit ~visited) |> List.split
@@ -379,7 +357,6 @@ let rec converterIsIdentity ~config ~toJS converter =
379357
argConverter |> converterIsIdentity ~config ~toJS:(not toJS)
380358
| GroupConverter _ -> false)
381359
| IdentC -> true
382-
| NullableC c -> c |> converterIsIdentity ~config ~toJS
383360
| ObjectC fieldsC ->
384361
fieldsC
385362
|> List.for_all (fun {lblJS; lblRE; c} ->
@@ -388,9 +365,8 @@ let rec converterIsIdentity ~config ~toJS converter =
388365
match c with
389366
| OptionC c1 -> c1 |> converterIsIdentity ~config ~toJS
390367
| _ -> c |> converterIsIdentity ~config ~toJS)
391-
| OptionC c -> if toJS then c |> converterIsIdentity ~config ~toJS else false
368+
| OptionC c -> c |> converterIsIdentity ~config ~toJS
392369
| PromiseC c -> c |> converterIsIdentity ~config ~toJS
393-
| RecordC _ -> false
394370
| TupleC innerTypesC ->
395371
innerTypesC |> List.for_all (converterIsIdentity ~config ~toJS)
396372
| VariantC {withPayloads; useVariantTables} ->
@@ -518,13 +494,6 @@ let rec apply ~config ~converter ~indent ~nameGen ~toJS ~variantTables value =
518494
EmitText.funDef ~bodyArgs ~functionName:componentName ~funParams ~indent
519495
~mkBody ~typeVars
520496
| IdentC -> value
521-
| NullableC c ->
522-
EmitText.parens
523-
[
524-
value ^ " == null ? " ^ value ^ " : "
525-
^ (value
526-
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables);
527-
]
528497
| ObjectC fieldsC ->
529498
let simplifyFieldConverted fieldConverter =
530499
match fieldConverter with
@@ -553,62 +522,19 @@ let rec apply ~config ~converter ~indent ~nameGen ~toJS ~variantTables value =
553522
in
554523
"{" ^ fieldValues ^ "}"
555524
| OptionC c ->
556-
if toJS then
557-
EmitText.parens
558-
[
559-
value ^ " == null ? " ^ value ^ " : "
560-
^ (value
561-
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables
562-
);
563-
]
564-
else
565-
EmitText.parens
566-
[
567-
value ^ " == null ? undefined : "
568-
^ (value
569-
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables
570-
);
571-
]
525+
EmitText.parens
526+
[
527+
value ^ " == null ? " ^ value ^ " : "
528+
^ (value
529+
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables);
530+
]
572531
| PromiseC c ->
573532
let x = "$promise" |> EmitText.name ~nameGen in
574533
value ^ ".then(function _element("
575534
^ (x |> EmitType.ofTypeAny ~config)
576535
^ ") { return "
577536
^ (x |> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables)
578537
^ "})"
579-
| RecordC fieldsC ->
580-
let simplifyFieldConverted fieldConverter =
581-
match fieldConverter with
582-
| OptionC converter1 when converter1 |> converterIsIdentity ~config ~toJS
583-
->
584-
IdentC
585-
| _ -> fieldConverter
586-
in
587-
if toJS then
588-
let fieldValues =
589-
fieldsC
590-
|> List.mapi (fun index {lblJS; c = fieldConverter} ->
591-
lblJS ^ ":"
592-
^ (value
593-
|> EmitText.arrayAccess ~index
594-
|> apply ~config
595-
~converter:(fieldConverter |> simplifyFieldConverted)
596-
~indent ~nameGen ~toJS ~variantTables))
597-
|> String.concat ", "
598-
in
599-
"{" ^ fieldValues ^ "}"
600-
else
601-
let fieldValues =
602-
fieldsC
603-
|> List.map (fun {lblJS; c = fieldConverter} ->
604-
value
605-
|> EmitText.fieldAccess ~label:lblJS
606-
|> apply ~config
607-
~converter:(fieldConverter |> simplifyFieldConverted)
608-
~indent ~nameGen ~toJS ~variantTables)
609-
|> String.concat ", "
610-
in
611-
"[" ^ fieldValues ^ "]"
612538
| TupleC innerTypesC ->
613539
"["
614540
^ (innerTypesC

jscomp/gentype/EmitJs.ml

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ let typeNameIsInterface ~(exportTypeMap : CodeItem.exportTypeMap)
101101
~(exportTypeMapFromOtherFiles : CodeItem.exportTypeMap) typeName =
102102
let typeIsInterface type_ =
103103
match type_ with
104-
| Object _ | Record _ -> true
104+
| Object _ -> true
105105
| _ -> false
106106
in
107107
match exportTypeMap |> StringMap.find typeName with
@@ -620,11 +620,11 @@ let propagateAnnotationToSubTypes ~codeItems (typeMap : CodeItem.exportTypeMap)
620620
type1 |> visit
621621
| exception Not_found ->
622622
annotatedSet := !annotatedSet |> StringSet.add typeName)
623-
| Array (t, _) -> t |> visit
623+
| Array (t, _) | Dict t -> t |> visit
624624
| Function {argTypes; retType} ->
625625
argTypes |> List.iter (fun {aType} -> visit aType);
626626
retType |> visit
627-
| GroupOfLabeledArgs fields | Object (_, fields) | Record fields ->
627+
| GroupOfLabeledArgs fields | Object (_, fields) ->
628628
fields |> List.iter (fun {type_} -> type_ |> visit)
629629
| Option t | Null t | Nullable t | Promise t -> t |> visit
630630
| Tuple innerTypes -> innerTypes |> List.iter visit

jscomp/gentype/EmitType.ml

+16-2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
9595
arrayName ^ "<"
9696
^ (t |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
9797
^ ">"
98+
| Dict type_ ->
99+
"{[id: string]: "
100+
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
101+
^ "}"
98102
| Function
99103
{argTypes = [{aType = Object (closedFlag, fields)}]; retType; typeVars}
100104
when retType |> isTypeFunctionComponent ~fields ->
@@ -116,7 +120,7 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
116120
| Function {argTypes; retType; typeVars} ->
117121
renderFunType ~config ~indent ~inFunType ~typeNameIsInterface ~typeVars
118122
argTypes retType
119-
| GroupOfLabeledArgs fields | Object (_, fields) | Record fields ->
123+
| GroupOfLabeledArgs fields | Object (_, fields) ->
120124
let indent1 = fields |> Indent.heuristicFields ~indent in
121125
fields
122126
|> renderFields ~config ~indent:indent1 ~inFunType ~typeNameIsInterface
@@ -136,7 +140,7 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
136140
"(null | "
137141
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
138142
^ ")"
139-
| Nullable type_ | Option type_ ->
143+
| Nullable type_ ->
140144
let useParens x =
141145
match type_ with
142146
| Function _ | Variant _ -> EmitText.parens [x]
@@ -146,6 +150,16 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
146150
^ useParens
147151
(type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
148152
^ ")"
153+
| Option type_ ->
154+
let useParens x =
155+
match type_ with
156+
| Function _ | Variant _ -> EmitText.parens [x]
157+
| _ -> x
158+
in
159+
"(undefined | "
160+
^ useParens
161+
(type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
162+
^ ")"
149163
| Promise type_ ->
150164
"Promise" ^ "<"
151165
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)

jscomp/gentype/GenTypeCommon.ml

+8-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type closedFlag = Open | Closed
5959

6060
type type_ =
6161
| Array of type_ * mutable_
62+
| Dict of type_
6263
| Function of function_
6364
| GroupOfLabeledArgs of fields
6465
| Ident of ident
@@ -67,7 +68,6 @@ type type_ =
6768
| Object of closedFlag * fields
6869
| Option of type_
6970
| Promise of type_
70-
| Record of fields
7171
| Tuple of type_ list
7272
| TypeVar of string
7373
| Variant of variant
@@ -108,6 +108,7 @@ and payload = {case: case; inlineRecord: bool; numArgs: int; t: type_}
108108
let typeIsObject type_ =
109109
match type_ with
110110
| Array _ -> true
111+
| Dict _ -> true
111112
| Function _ -> false
112113
| GroupOfLabeledArgs _ -> false
113114
| Ident _ -> false
@@ -116,7 +117,6 @@ let typeIsObject type_ =
116117
| Object _ -> true
117118
| Option _ -> false
118119
| Promise _ -> true
119-
| Record _ -> true
120120
| Tuple _ -> true
121121
| TypeVar _ -> false
122122
| Variant _ -> false
@@ -210,11 +210,17 @@ let sanitizeTypeName name =
210210
| '\'' -> '_'
211211
| c -> c)
212212
let unknown = ident "unknown"
213+
let bigintT = ident "BigInt"
213214
let booleanT = ident "boolean"
214215
let dateT = ident "Date"
216+
let mapT (x,y) = ident ~typeArgs:[x;y] "Map"
215217
let numberT = ident "number"
218+
let regexpT = ident "RegExp"
219+
let setT (x) = ident ~typeArgs:[x] "Set"
216220
let stringT = ident "string"
217221
let unitT = ident "void"
222+
let weakmapT (x,y) = ident ~typeArgs:[x;y] "WeakMap"
223+
let weaksetT (x) = ident ~typeArgs:[x] "WeakSet"
218224
let int64T = Tuple [numberT; numberT]
219225

220226
module NodeFilename = struct

jscomp/gentype/TranslateSignatureFromTypes.ml

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ let translateTypeDeclarationFromTypes ~config ~outputFileRelative ~resolver
1212
Log_.item "Translate Types.type_declaration %s\n" typeName;
1313
let declarationKind =
1414
match type_kind with
15-
| Type_record (labelDeclarations, _) ->
16-
TranslateTypeDeclarations.RecordDeclarationFromTypes labelDeclarations
15+
| Type_record (labelDeclarations, recordRepresentation) ->
16+
TranslateTypeDeclarations.RecordDeclarationFromTypes
17+
(labelDeclarations, recordRepresentation)
1718
| Type_variant constructorDeclarations
1819
when not
1920
(TranslateTypeDeclarations.hasSomeGADTLeaf constructorDeclarations)

jscomp/gentype/TranslateTypeDeclarations.ml

+19-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
open GenTypeCommon
22

33
type declarationKind =
4-
| RecordDeclarationFromTypes of Types.label_declaration list
4+
| RecordDeclarationFromTypes of
5+
Types.label_declaration list * Types.record_representation
56
| GeneralDeclaration of Typedtree.core_type option
67
| GeneralDeclarationFromTypes of Types.type_expr option
78
(** As the above, but from Types not Typedtree *)
@@ -78,7 +79,12 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
7879
in
7980
{CodeItem.importTypes; exportFromTypeDeclaration}
8081
in
81-
let translateLabelDeclarations labelDeclarations =
82+
let translateLabelDeclarations ~recordRepresentation labelDeclarations =
83+
let isOptional l =
84+
match recordRepresentation with
85+
| Types.Record_optional_labels lbls -> List.mem l lbls
86+
| _ -> false
87+
in
8288
let fieldTranslations =
8389
labelDeclarations
8490
|> List.map (fun {Types.ld_id; ld_mutable; ld_type; ld_attributes} ->
@@ -111,7 +117,7 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
111117
->
112118
let optional, type1 =
113119
match type_ with
114-
| Option type1 -> (Optional, type1)
120+
| Option type1 when isOptional nameRE -> (Optional, type1)
115121
| _ -> (Mandatory, type_)
116122
in
117123
{mutable_; nameJS; nameRE; optional; type_ = type1})
@@ -196,9 +202,10 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
196202
in
197203
{translation with type_} |> handleGeneralDeclaration
198204
|> returnTypeDeclaration
199-
| RecordDeclarationFromTypes labelDeclarations, None ->
205+
| RecordDeclarationFromTypes (labelDeclarations, recordRepresentation), None
206+
->
200207
let {TranslateTypeExprFromTypes.dependencies; type_} =
201-
labelDeclarations |> translateLabelDeclarations
208+
labelDeclarations |> translateLabelDeclarations ~recordRepresentation
202209
in
203210
let importTypes =
204211
dependencies
@@ -227,7 +234,11 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
227234
|> TranslateTypeExprFromTypes.translateTypeExprsFromTypes
228235
~config ~typeEnv
229236
| Cstr_record labelDeclarations ->
230-
[labelDeclarations |> translateLabelDeclarations]
237+
[
238+
labelDeclarations
239+
|> translateLabelDeclarations
240+
~recordRepresentation:Types.Record_regular;
241+
]
231242
in
232243
let inlineRecord =
233244
match constructorArgs with
@@ -342,8 +353,8 @@ let translateTypeDeclaration ~config ~outputFileRelative ~recursive ~resolver
342353
in
343354
let declarationKind =
344355
match typ_type.type_kind with
345-
| Type_record (labelDeclarations, _) ->
346-
RecordDeclarationFromTypes labelDeclarations
356+
| Type_record (labelDeclarations, recordRepresentation) ->
357+
RecordDeclarationFromTypes (labelDeclarations, recordRepresentation)
347358
| Type_variant constructorDeclarations ->
348359
VariantDeclarationFromTypes constructorDeclarations
349360
| Type_abstract -> GeneralDeclaration typ_manifest

0 commit comments

Comments
 (0)