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
Copy file name to clipboardExpand all lines: pages/docs/manual/latest/record.mdx
+245-2Lines changed: 245 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,7 +20,7 @@ A record needs a mandatory type declaration:
20
20
```res prelude
21
21
type person = {
22
22
age: int,
23
-
name: string
23
+
name: string,
24
24
}
25
25
```
26
26
```js
@@ -157,9 +157,247 @@ Fields not marked with `mutable` in the type declaration cannot be mutated.
157
157
158
158
ReScript records compile to straightforward JavaScript objects; see the various JS output tabs above.
159
159
160
+
## Optional Record Fields
161
+
ReScript [`v10`](/blog/release-10-0-0#experimental-optional-record-fields) introduced optional record fields. This means that you can define fields that can be omitted when creating the record. It looks like this:
162
+
163
+
<CodeTablabels={["ReScript", "JS Output"]}>
164
+
165
+
```res prelude
166
+
type person = {
167
+
age: int,
168
+
name?: string
169
+
}
170
+
```
171
+
```js
172
+
// Empty output
173
+
```
174
+
175
+
</CodeTab>
176
+
177
+
Notice how `name` has a suffixed `?`. That means that the field itself is _optional_.
178
+
179
+
### Creation
180
+
You can omit any optional fields when creating a record. Not setting an optional field will default the field's value to `None`:
181
+
182
+
<CodeTablabels={["ReScript", "JS Output"]}>
183
+
184
+
```res prelude
185
+
type person = {
186
+
age: int,
187
+
name?: string
188
+
}
189
+
190
+
let me = {
191
+
age: 5,
192
+
name: "Big ReScript"
193
+
}
194
+
195
+
let friend = {
196
+
age: 7
197
+
}
198
+
```
199
+
```js
200
+
var me = {
201
+
age:5,
202
+
name:"Big ReScript"
203
+
};
204
+
205
+
var friend = {
206
+
age:7
207
+
};
208
+
```
209
+
210
+
</CodeTab>
211
+
212
+
This has consequences for pattern matching, which we'll expand a bit on soon.
213
+
214
+
## Immutable Update
215
+
Updating an optional field via an immutable update above lets you set that field value without needing to care whether it's optional or not.
216
+
217
+
<CodeTablabels={["ReScript", "JS Output"]}>
218
+
219
+
```res example
220
+
type person = {
221
+
age: int,
222
+
name?: string
223
+
}
224
+
225
+
let me = {
226
+
age: 123,
227
+
name: "Hello"
228
+
}
229
+
230
+
let withoutName = {
231
+
...me,
232
+
name: "New Name"
233
+
}
234
+
```
235
+
```js
236
+
import*asCaml_objfrom"./stdlib/caml_obj.js";
237
+
238
+
var me = {
239
+
age:123,
240
+
name:"Hello"
241
+
};
242
+
243
+
var newrecord =Caml_obj.obj_dup(me);
244
+
245
+
newrecord.name="New Name";
246
+
247
+
var withoutName = newrecord;
248
+
```
249
+
250
+
</CodeTab>
251
+
252
+
253
+
However, if you want to set the field to an optional value, you prefix that value with `?`:
254
+
255
+
<CodeTablabels={["ReScript", "JS Output"]}>
256
+
257
+
```res example
258
+
type person = {
259
+
age: int,
260
+
name?: string
261
+
}
262
+
263
+
let me = {
264
+
age: 123,
265
+
name: "Hello"
266
+
}
267
+
268
+
let maybeName = Some("My Name")
269
+
270
+
let withoutName = {
271
+
...me,
272
+
name: ?maybeName
273
+
}
274
+
```
275
+
```js
276
+
import*asCaml_objfrom"./stdlib/caml_obj.js";
277
+
278
+
var me = {
279
+
age:123,
280
+
name:"Hello"
281
+
};
282
+
283
+
var maybeName ="My Name";
284
+
285
+
var newrecord =Caml_obj.obj_dup(me);
286
+
287
+
newrecord.name= maybeName;
288
+
289
+
var withoutName = newrecord;
290
+
```
291
+
292
+
</CodeTab>
293
+
294
+
You can unset an optional field's value via that same mechanism by setting it to `?None`.
295
+
296
+
### Pattern Matching on Optional Fields
297
+
[Pattern matching](pattern-matching-destructuring), one of ReScript's most important features, has two caveats when you deal with optional fields.
298
+
299
+
When matching on the value directly, it's an `option`. Example:
300
+
301
+
<CodeTablabels={["ReScript", "JS Output"]}>
302
+
303
+
```res
304
+
type person = {
305
+
age: int,
306
+
name?: string,
307
+
}
308
+
309
+
let me = {
310
+
age: 123,
311
+
name: "Hello",
312
+
}
313
+
314
+
let isRescript = switch me.name {
315
+
| Some("ReScript") => true
316
+
| Some(_) | None => false
317
+
}
318
+
```
319
+
```js
320
+
var isRescript;
321
+
322
+
isRescript ="Hello"==="ReScript"?true:false;
323
+
324
+
var me = {
325
+
age:123,
326
+
name:"Hello"
327
+
};
328
+
```
329
+
330
+
</CodeTab>
331
+
332
+
But, when matching on the field as part of the general record structure, it's treated as the underlying, non-optional value:
333
+
334
+
<CodeTablabels={["ReScript", "JS Output"]}>
335
+
336
+
```res
337
+
type person = {
338
+
age: int,
339
+
name?: string,
340
+
}
341
+
342
+
let me = {
343
+
age: 123,
344
+
name: "Hello",
345
+
}
346
+
347
+
let isRescript = switch me {
348
+
| {name: "ReScript"} => true
349
+
| _ => false
350
+
}
351
+
352
+
```
353
+
```js
354
+
var isRescript;
355
+
356
+
isRescript ="Hello"==="ReScript"?true:false;
357
+
358
+
var me = {
359
+
age:123,
360
+
name:"Hello"
361
+
};
362
+
```
363
+
364
+
</CodeTab>
365
+
366
+
Sometimes you _do_ want to know whether the field was set or not. You can tell the pattern matching engine about that by prefixing your option match with `?`, like this:
367
+
368
+
<CodeTablabels={["ReScript", "JS Output"]}>
369
+
370
+
```res
371
+
type person = {
372
+
age: int,
373
+
name?: string,
374
+
}
375
+
376
+
let me = {
377
+
age: 123,
378
+
name: "Hello",
379
+
}
380
+
381
+
let nameWasSet = switch me {
382
+
| {name: ?None} => false
383
+
| {name: ?Some(_)} => true
384
+
}
385
+
```
386
+
```js
387
+
var nameWasSet =false;
388
+
389
+
var me = {
390
+
age:123,
391
+
name:"Hello"
392
+
};
393
+
```
394
+
395
+
</CodeTab>
396
+
160
397
## Tips & Tricks
161
398
162
-
**Record Types Are Found By Field Name**. With records, you **cannot** say "I'd like this function to take any record type, as long as they have the field `age`". The following **won't work as intended**:
399
+
### Record Types Are Found By Field Name
400
+
With records, you **cannot** say "I'd like this function to take any record type, as long as they have the field `age`". The following **won't work as intended**:
163
401
164
402
<CodeTablabels={["ReScript", "JS Output"]}>
165
403
@@ -189,6 +427,11 @@ getAge(me) // type error!
189
427
190
428
The type system will complain that `me` is a `person`, and that `getAge` only works on `monster`. If you need such capability, use ReScript objects, described [here](object.md).
191
429
430
+
### Optional Fields in Records Can Be Useful for Bindings
431
+
Many JavaScript APIs tend to have large configuration objects that can be a bit annoying to model as records, since you previously always needed to specify all record fields when creating a record.
432
+
433
+
Optional record fields, introduced in [`v10`](/blog/release-10-0-0#experimental-optional-record-fields), is intended to help with this. Optional fields will let you avoid having to specify all fields, and let you just specify the one's you care about. A significant improvement in ergonomics for bindings and other APIs with for example large configuration objects.
434
+
192
435
## Design Decisions
193
436
194
437
After reading the constraints in the previous sections, and if you're coming from a dynamic language background, you might be wondering why one would bother with record in the first place instead of straight using object, since the former needs explicit typing and doesn't allow different records with the same field name to be passed to the same function, etc.
0 commit comments