From 7da0d96b1effd4302aab03460999e7973162f6fb Mon Sep 17 00:00:00 2001 From: jmagaram Date: Mon, 13 Mar 2023 20:57:04 -0700 Subject: [PATCH 01/34] Object.is documentation --- src/Core__Object.res | 35 +++- test/ObjectTests.mjs | 437 +++++++++++++++++++++++++++++++++++++++++++ test/ObjectTests.res | 56 ++++++ test/TestSuite.mjs | 19 +- test/TestSuite.res | 1 + 5 files changed, 544 insertions(+), 4 deletions(-) create mode 100644 test/ObjectTests.mjs create mode 100644 test/ObjectTests.res diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..bd72a01c 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,6 +1,39 @@ @obj external empty: unit => {..} = "" -@val external is: ('a, 'b) => bool = "Object.is" +/** +`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, bools, and strings are identical if they have the same value. + +**Note:** In most scenarios use `==` and `===`. If the data type you want to compare by value has an `equals` function, use it. + +The `==` operator is different in ReScript than Javascript. Non-primitives like arrays and records are considered equal if they have the same contents. + +In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar to (but not idential to) `Object.is`. See the references. + +## Examples +```rescript +Object.is(25, 13) // false +Object.is("abc", "abc") // true +Object.is(undefined, undefined) // true +Object.is(undefined, null) // false +Object.is(-0.0, 0.0) // false +Object.is(list{1, 2}, list{1, 2}) // false + +let fruit = {"name": "Apple" } +Object.is(fruit, fruit) // true +Object.is(fruit, {"name": "Apple" }) // false +fruit == {"name": "Apple" } // true +fruit === {"name": "Apple" } // false + +Object.is([1, 2, 3], [1, 2, 3]) // false +[1, 2, 3] == [1, 2, 3] // true +[1, 2, 3] === [1, 2, 3] // false +``` +## Specifications +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.is) +- [`Object.is on Mozilla`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +*/ +@val +external is: ('a, 'b) => bool = "Object.is" @val external create: {..} => {..} = "Object.create" @val external createWithProperties: ({..}, {..}) => {..} = "Object.create" diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs new file mode 100644 index 00000000..0d1cdd8a --- /dev/null +++ b/test/ObjectTests.mjs @@ -0,0 +1,437 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Test from "./Test.mjs"; +import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; + +var eq = Caml_obj.equal; + +Test.run([ + [ + "ObjectTests.res", + 7, + 20, + 41 + ], + "is: different types" + ], Object.is("abc", false), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 9, + 20, + 30 + ], + "is: ints" + ], Object.is(25, 25), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 11, + 20, + 33 + ], + "is: strings" + ], Object.is("abc", "abc"), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 12, + 20, + 33 + ], + "is: strings" + ], Object.is("abc", "ABC"), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 14, + 20, + 44 + ], + "is: null and undefined" + ], Object.is(null, undefined), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 15, + 20, + 44 + ], + "is: null and undefined" + ], Object.is(undefined, undefined), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 16, + 20, + 44 + ], + "is: null and undefined" + ], Object.is(null, null), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 17, + 20, + 44 + ], + "is: null and undefined" + ], Object.is(null, undefined), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 18, + 20, + 44 + ], + "is: undefined and None" + ], Object.is(undefined, undefined), eq, true); + +var nums = [ + 1, + 2, + 3 +]; + +Test.run([ + [ + "ObjectTests.res", + 21, + 20, + 32 + ], + "is: arrays" + ], Object.is([ + 1, + 2, + 3 + ], [ + 1, + 2, + 3 + ]), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 22, + 20, + 32 + ], + "is: arrays" + ], Object.is(nums, nums), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 23, + 20, + 32 + ], + "is: arrays" + ], Caml_obj.equal([ + 1, + 2, + 3 + ], [ + 1, + 2, + 3 + ]), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 24, + 20, + 32 + ], + "is: arrays" + ], [ + 1, + 2, + 3 + ] === [ + 1, + 2, + 3 + ], eq, false); + +Test.run([ + [ + "ObjectTests.res", + 26, + 20, + 30 + ], + "is: list" + ], Object.is({ + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }, { + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 27, + 20, + 30 + ], + "is: list" + ], Caml_obj.equal({ + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }, { + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 28, + 20, + 30 + ], + "is: list" + ], ({ + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }) === ({ + hd: 1, + tl: { + hd: 2, + tl: { + hd: 3, + tl: /* [] */0 + } + } + }), eq, false); + +var d = new Date(2000, 1); + +Test.run([ + [ + "ObjectTests.res", + 32, + 13, + 23 + ], + "is: date" + ], Object.is(new Date(2000, 1), new Date(2000, 1)), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 37, + 20, + 30 + ], + "is: date" + ], Object.is(d, d), eq, true); + +var x = { + a: 1 +}; + +Test.run([ + [ + "ObjectTests.res", + 40, + 20, + 33 + ], + "is: objects" + ], Object.is(x, x), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 41, + 20, + 33 + ], + "is: objects" + ], Object.is({ + a: 1 + }, { + a: 1 + }), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 42, + 20, + 33 + ], + "is: objects" + ], Object.is({}, {}), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 43, + 20, + 45 + ], + "is: === and == operator" + ], x === x, eq, true); + +Test.run([ + [ + "ObjectTests.res", + 44, + 20, + 45 + ], + "is: === and == operator" + ], Caml_obj.equal(x, x), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 45, + 20, + 45 + ], + "is: === and == operator" + ], Caml_obj.equal({ + a: 1 + }, { + a: 1 + }), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 47, + 20, + 31 + ], + "is: zeros" + ], Object.is(0, 0), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 48, + 20, + 31 + ], + "is: zeros" + ], Object.is(-0.0, -0.0), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 49, + 20, + 31 + ], + "is: zeros" + ], Object.is(0.0, -0.0), eq, false); + +function mkBig(s) { + return BigInt(s); +} + +Test.run([ + [ + "ObjectTests.res", + 52, + 20, + 32 + ], + "is: bigint" + ], Object.is(BigInt("123456789"), BigInt("123456789")), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 53, + 20, + 32 + ], + "is: bigint" + ], Object.is(BigInt("123489"), BigInt("123456789")), eq, false); + +Test.run([ + [ + "ObjectTests.res", + 54, + 20, + 32 + ], + "is: bigint" + ], Object.is(BigInt("000000000"), BigInt("0")), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 55, + 20, + 32 + ], + "is: bigint" + ], Caml_obj.equal(BigInt("123"), BigInt("123")), eq, true); + +Test.run([ + [ + "ObjectTests.res", + 56, + 20, + 32 + ], + "is: bigint" + ], BigInt("123") === BigInt("123"), eq, true); + +export { + eq , + nums , + d , + x , + mkBig , +} +/* Not a pure module */ diff --git a/test/ObjectTests.res b/test/ObjectTests.res new file mode 100644 index 00000000..ad6551ac --- /dev/null +++ b/test/ObjectTests.res @@ -0,0 +1,56 @@ +open RescriptCore + +let eq = (a, b) => a == b + +// ===== is ===== + +Test.run(__POS_OF__("is: different types"), Object.is("abc", false), eq, false) + +Test.run(__POS_OF__("is: ints"), Object.is(25, 25), eq, true) + +Test.run(__POS_OF__("is: strings"), Object.is("abc", "abc"), eq, true) +Test.run(__POS_OF__("is: strings"), Object.is("abc", "ABC"), eq, false) + +Test.run(__POS_OF__("is: null and undefined"), Object.is(null, undefined), eq, false) +Test.run(__POS_OF__("is: null and undefined"), Object.is(undefined, undefined), eq, true) +Test.run(__POS_OF__("is: null and undefined"), Object.is(null, null), eq, true) +Test.run(__POS_OF__("is: null and undefined"), Object.is(null, None), eq, false) +Test.run(__POS_OF__("is: undefined and None"), Object.is(undefined, None), eq, true) // hmmm... + +let nums = [1, 2, 3] +Test.run(__POS_OF__("is: arrays"), Object.is([1, 2, 3], [1, 2, 3]), eq, false) +Test.run(__POS_OF__("is: arrays"), Object.is(nums, nums), eq, true) +Test.run(__POS_OF__("is: arrays"), [1, 2, 3] == [1, 2, 3], eq, true) +Test.run(__POS_OF__("is: arrays"), [1, 2, 3] === [1, 2, 3], eq, false) + +Test.run(__POS_OF__("is: list"), Object.is(list{1, 2, 3}, list{1, 2, 3}), eq, false) +Test.run(__POS_OF__("is: list"), list{1, 2, 3} == list{1, 2, 3}, eq, true) +Test.run(__POS_OF__("is: list"), list{1, 2, 3} === list{1, 2, 3}, eq, false) + +let d = Date.makeWithYM(~year=2000, ~month=1) +Test.run( + __POS_OF__("is: date"), + Object.is(Date.makeWithYM(~year=2000, ~month=1), Date.makeWithYM(~year=2000, ~month=1)), + eq, + false, +) +Test.run(__POS_OF__("is: date"), Object.is(d, d), eq, true) + +let x = {"a": 1} +Test.run(__POS_OF__("is: objects"), Object.is(x, x), eq, true) +Test.run(__POS_OF__("is: objects"), Object.is({"a": 1}, {"a": 1}), eq, false) +Test.run(__POS_OF__("is: objects"), Object.is(Object.empty(), Object.empty()), eq, false) // hmm... +Test.run(__POS_OF__("is: === and == operator"), x === x, eq, true) +Test.run(__POS_OF__("is: === and == operator"), x == x, eq, true) +Test.run(__POS_OF__("is: === and == operator"), {"a": 1} == {"a": 1}, eq, true) + +Test.run(__POS_OF__("is: zeros"), Object.is(-0, -0), eq, true) +Test.run(__POS_OF__("is: zeros"), Object.is(-0.0, -0.0), eq, true) +Test.run(__POS_OF__("is: zeros"), Object.is(0.0, -0.0), eq, false) + +let mkBig = s => BigInt.fromString(s) +Test.run(__POS_OF__("is: bigint"), Object.is(mkBig("123456789"), mkBig("123456789")), eq, true) +Test.run(__POS_OF__("is: bigint"), Object.is(mkBig("123489"), mkBig("123456789")), eq, false) +Test.run(__POS_OF__("is: bigint"), Object.is(mkBig("000000000"), mkBig("0")), eq, true) +Test.run(__POS_OF__("is: bigint"), mkBig("123") == mkBig("123"), eq, true) +Test.run(__POS_OF__("is: bigint"), mkBig("123") === mkBig("123"), eq, true) diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index c968bd8f..f5084781 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -4,6 +4,7 @@ import * as IntTests from "./IntTests.mjs"; import * as TestTests from "./TestTests.mjs"; import * as ArrayTests from "./ArrayTests.mjs"; import * as ErrorTests from "./ErrorTests.mjs"; +import * as ObjectTests from "./ObjectTests.mjs"; import * as PromiseTest from "./PromiseTest.mjs"; var bign = TestTests.bign; @@ -26,10 +27,18 @@ var Concurrently = PromiseTest.Concurrently; var panicTest = ErrorTests.panicTest; -var eq = IntTests.eq; - var $$catch = IntTests.$$catch; +var eq = ObjectTests.eq; + +var nums = ObjectTests.nums; + +var d = ObjectTests.d; + +var x = ObjectTests.x; + +var mkBig = ObjectTests.mkBig; + export { bign , TestError , @@ -41,7 +50,11 @@ export { Catching , Concurrently , panicTest , - eq , $$catch , + eq , + nums , + d , + x , + mkBig , } /* IntTests Not a pure module */ diff --git a/test/TestSuite.res b/test/TestSuite.res index 6277bf57..ca2a63ca 100644 --- a/test/TestSuite.res +++ b/test/TestSuite.res @@ -3,3 +3,4 @@ include PromiseTest include ErrorTests include ArrayTests include IntTests +include ObjectTests From da6197a150f14b0fb89e49d319e4eca6f8828609 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Mon, 13 Mar 2023 22:00:07 -0700 Subject: [PATCH 02/34] reference to rescript docs, +0/-0, small tweaks --- src/Core__Object.res | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index bd72a01c..f968ffaf 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,13 +1,14 @@ @obj external empty: unit => {..} = "" /** -`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, bools, and strings are identical if they have the same value. +`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is +equal to itself. -**Note:** In most scenarios use `==` and `===`. If the data type you want to compare by value has an `equals` function, use it. +**Note:** In most scenarios use `==` and `===`. If the type you want to compare by value has an `equals` function, use it. -The `==` operator is different in ReScript than Javascript. Non-primitives like arrays and records are considered equal if they have the same contents. +The `==` operator [is different in ReScript than Javascript](https://rescript-lang.org/docs/manual/latest/overview#boolean). Arrays, records and other non-primitives are equal if they have the same contents (deep equality). -In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar to (but not idential to) `Object.is`. See the references. +In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar but not identical to `Object.is`; see [Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ## Examples ```rescript @@ -31,6 +32,7 @@ Object.is([1, 2, 3], [1, 2, 3]) // false ## Specifications - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.is) - [`Object.is on Mozilla`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +- [Equality operators in ReScript](https://rescript-lang.org/docs/manual/latest/overview#boolean) */ @val external is: ('a, 'b) => bool = "Object.is" From 8f1d753f78fac3146dbbc009f4a7ac40ff593b66 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Mon, 13 Mar 2023 22:03:22 -0700 Subject: [PATCH 03/34] unnecessary paragraph mark --- src/Core__Object.res | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index f968ffaf..4a790343 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,8 +1,7 @@ @obj external empty: unit => {..} = "" /** -`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is -equal to itself. +`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. **Note:** In most scenarios use `==` and `===`. If the type you want to compare by value has an `equals` function, use it. From 5db75f0dd221d0ac0efc040e5ec2594d1981362e Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 14 Mar 2023 10:33:31 -0700 Subject: [PATCH 04/34] cleaned up a bit --- src/Core__Object.res | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 4a790343..357e62a5 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,7 +1,7 @@ @obj external empty: unit => {..} = "" /** -`Object.is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. +`is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. **Note:** In most scenarios use `==` and `===`. If the type you want to compare by value has an `equals` function, use it. @@ -10,6 +10,7 @@ The `==` operator [is different in ReScript than Javascript](https://rescript-la In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar but not identical to `Object.is`; see [Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ## Examples + ```rescript Object.is(25, 13) // false Object.is("abc", "abc") // true @@ -18,17 +19,18 @@ Object.is(undefined, null) // false Object.is(-0.0, 0.0) // false Object.is(list{1, 2}, list{1, 2}) // false +Object.is([1, 2, 3], [1, 2, 3]) // false +[1, 2, 3] == [1, 2, 3] // true +[1, 2, 3] === [1, 2, 3] // false + let fruit = {"name": "Apple" } Object.is(fruit, fruit) // true Object.is(fruit, {"name": "Apple" }) // false fruit == {"name": "Apple" } // true fruit === {"name": "Apple" } // false - -Object.is([1, 2, 3], [1, 2, 3]) // false -[1, 2, 3] == [1, 2, 3] // true -[1, 2, 3] === [1, 2, 3] // false ``` ## Specifications + - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.is) - [`Object.is on Mozilla`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) - [Equality operators in ReScript](https://rescript-lang.org/docs/manual/latest/overview#boolean) From 2741d73371690e70d16a5f3c94eb3efebd402e8e Mon Sep 17 00:00:00 2001 From: jmagaram Date: Wed, 15 Mar 2023 12:27:37 -0700 Subject: [PATCH 05/34] change recommendation text --- src/Core__Object.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 357e62a5..89c8f69f 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -3,7 +3,7 @@ /** `is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. -**Note:** In most scenarios use `==` and `===`. If the type you want to compare by value has an `equals` function, use it. +**Note:** In most scenarios use `==` or `===` or the custom `equals` function (if provided) for the type. The `==` operator [is different in ReScript than Javascript](https://rescript-lang.org/docs/manual/latest/overview#boolean). Arrays, records and other non-primitives are equal if they have the same contents (deep equality). From 033d0a6f91c19a23d1f9a713e49527f2affcdc01 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 16 Mar 2023 12:11:30 -0700 Subject: [PATCH 06/34] docs for object.freeze and object.isfrozen --- src/Core__Object.res | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..d07f951c 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -24,8 +24,46 @@ @val external seal: 'a => 'a = "Object.seal" @val external preventExtensions: 'a => 'a = "Object.preventExtensions" -@val external freeze: 'a => 'a = "Object.freeze" +/** +`freeze` freezes an object. Freezing an object makes existing properties non-writable and prevents extensions. Once an object is frozen, new properties cannot be be added, existing properties cannot be removed, and their values cannot be changed. `freeze` returns the same object that was passed in; it does not create a frozen copy. + +Any attempt to change a frozen object will fail, either silently or by throwing an exception. + +Rescript usually [disallows modifying objects](https://rescript-lang.org/docs/manual/latest/object#update) regardless of whether they are frozen. + + ## Examples + + ```rescript +let point = {"x": 1, "y": 3}->Object.freeze +let pointIsFrozen = point->Object.isFrozen // true +let fruit = {"name": "Apple" } +let fruitIsFrozen = fruit->Object.isFrozen // false + ``` + ## Specifications +- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.freeze) +- [Object.freeze on Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) + */ +@val +external freeze: 'a => 'a = "Object.freeze" @val external isSealed: 'a => bool = "Object.isSealed" -@val external isFrozen: 'a => bool = "Object.isFrozen" +/** +`isFrozen` determines if an object is frozen. An object is frozen if an only if it is not extensible, all its properties are non-configurable, and all its data properties are non-writable. + +## Examples + +```rescript +let point = {"x": 1, "y": 3}->Object.freeze +let pointIsFrozen = point->Object.isFrozen // true +let fruit = {"name": "Apple" } +let fruitIsFrozen = fruit->Object.isFrozen // false + ``` + ## Specifications +- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) +- [Object.isFrozen on Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) + */ +@val +external isFrozen: 'a => bool = "Object.isFrozen" @val external isExtensible: 'a => bool = "Object.isExtensible" From 5b95915a2fdf0655d664ed81f7a95c5202fef355 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 16 Mar 2023 22:42:13 -0700 Subject: [PATCH 07/34] seal and isSealed documentation --- src/Core__Object.res | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..45eda350 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -22,10 +22,48 @@ @val external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" -@val external seal: 'a => 'a = "Object.seal" +/** +`seal` seals an object. Sealing an object prevents extensions and makes existing properties non-configurable. A sealed object has a fixed set of properties. Unlike `freeze`, values of existing properties can still be changed as long as they are writable. `seal` returns the same object that was passed in. + +Any attempt to delete or add properties to a sealed object will fail, either silently or by throwing an error. + +Rescript usually [disallows modifying objects](https://rescript-lang.org/docs/manual/latest/object#update) regardless of whether they are sealed. + +## Examples + +```rescript +let point = {"x": 1, "y": 3}->Object.seal +let pointIsSealed = point->Object.isSealed // true +let fruit = {"name": "Apple" } +let fruitIsSealed = fruit->Object.isSealed // false +``` +## Specifications +- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.seal) +- [Object.seal on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) +*/ +@val +external seal: 'a => 'a = "Object.seal" @val external preventExtensions: 'a => 'a = "Object.preventExtensions" @val external freeze: 'a => 'a = "Object.freeze" -@val external isSealed: 'a => bool = "Object.isSealed" +/** + `isSealed` determines if an object is sealed. A sealed object has a fixed set of properties. + + ## Examples + + ```rescript + let point = {"x": 1, "y": 3}->Object.seal + let pointIsSealed = point->Object.isSealed // true + let fruit = {"name": "Apple" } + let fruitIsSealed = fruit->Object.isSealed // false + ``` + ## Specifications + - [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) + - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.issealed) + - [Object.isSealed on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) + */ +@val +external isSealed: 'a => bool = "Object.isSealed" @val external isFrozen: 'a => bool = "Object.isFrozen" @val external isExtensible: 'a => bool = "Object.isExtensible" From d60469f7870c4b219296f5f60d9039610472ed26 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 16 Mar 2023 22:44:46 -0700 Subject: [PATCH 08/34] MDN not Mozilla --- src/Core__Object.res | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index d07f951c..64165ee1 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -42,7 +42,7 @@ let fruitIsFrozen = fruit->Object.isFrozen // false ## Specifications - [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.freeze) -- [Object.freeze on Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) +- [Object.freeze on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) */ @val external freeze: 'a => 'a = "Object.freeze" @@ -62,7 +62,7 @@ let fruitIsFrozen = fruit->Object.isFrozen // false ## Specifications - [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) -- [Object.isFrozen on Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) +- [Object.isFrozen on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) */ @val external isFrozen: 'a => bool = "Object.isFrozen" From 8f6db7ce1996b92cb187fec6ba3d61e2fa41459e Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 16 Mar 2023 22:45:34 -0700 Subject: [PATCH 09/34] MDN not Mozilla --- src/Core__Object.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 89c8f69f..811840cb 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -32,7 +32,7 @@ fruit === {"name": "Apple" } // false ## Specifications - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.is) -- [`Object.is on Mozilla`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +- [`Object.is on MDN`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) - [Equality operators in ReScript](https://rescript-lang.org/docs/manual/latest/overview#boolean) */ @val From 6d36dbfa48fb8d7c85e19b1732f202557a941e59 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 16 Mar 2023 23:49:53 -0700 Subject: [PATCH 10/34] documentation for preventExtensions --- src/Core__Object.res | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..1e95ef89 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -23,9 +23,41 @@ @val external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" @val external seal: 'a => 'a = "Object.seal" -@val external preventExtensions: 'a => 'a = "Object.preventExtensions" +/** +`preventExtensions` prevents new properties from being added to the object. It modifies the object (rather than creating a copy) and returns it. + +## Examples + +```rescript +let obj = {"a": 1} +obj->Object.set("b", 2) // succeeds +obj->Object.preventExtensions->ignore +obj->Object.set("c", 3) // fails +``` +## Specifications +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.preventextensions) +- [Object.preventExtensions on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) +*/ +@val +external preventExtensions: 'a => 'a = "Object.preventExtensions" @val external freeze: 'a => 'a = "Object.freeze" @val external isSealed: 'a => bool = "Object.isSealed" @val external isFrozen: 'a => bool = "Object.isFrozen" -@val external isExtensible: 'a => bool = "Object.isExtensible" +/** + `isExtensible` determines if an object is extensible (whether it can have new properties added to it). + + ## Examples + + ```rescript +let obj = {"a": 1} +obj->Object.isExtensible // true +obj->Object.preventExtensions->ignore +obj->Object.isExtensible // false +``` +## Specifications +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isextensible) +- [Object.isExtensible on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) +*/ +@val +external isExtensible: 'a => bool = "Object.isExtensible" From 258abe30fe68e6fe7f276d1673356291b3f5d76f Mon Sep 17 00:00:00 2001 From: jmagaram Date: Fri, 17 Mar 2023 00:11:49 -0700 Subject: [PATCH 11/34] better docs and sample code --- src/Core__Object.res | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 64165ee1..2bc2f5a2 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -25,24 +25,22 @@ @val external seal: 'a => 'a = "Object.seal" @val external preventExtensions: 'a => 'a = "Object.preventExtensions" /** -`freeze` freezes an object. Freezing an object makes existing properties non-writable and prevents extensions. Once an object is frozen, new properties cannot be be added, existing properties cannot be removed, and their values cannot be changed. `freeze` returns the same object that was passed in; it does not create a frozen copy. - -Any attempt to change a frozen object will fail, either silently or by throwing an exception. +`freeze` freezes an object. Freezing an object makes existing properties non-writable and prevents extensions. Once an object is frozen, new properties cannot be be added, existing properties cannot be removed, and their values cannot be changed. -Rescript usually [disallows modifying objects](https://rescript-lang.org/docs/manual/latest/object#update) regardless of whether they are frozen. +**Note:** `freeze` returns the same object that was passed in; it does not create a frozen copy. Any attempt to change a frozen object will fail, either silently or by throwing an exception. ## Examples ```rescript -let point = {"x": 1, "y": 3}->Object.freeze -let pointIsFrozen = point->Object.isFrozen // true -let fruit = {"name": "Apple" } -let fruitIsFrozen = fruit->Object.isFrozen // false +let obj = {"a": 1} +obj->Object.set("a", 2) // succeeds +obj->Object.freeze->ignore +obj->Object.set("a", 3) // fails ``` ## Specifications -- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.freeze) - [Object.freeze on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) +- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) */ @val external freeze: 'a => 'a = "Object.freeze" @@ -60,9 +58,9 @@ let fruit = {"name": "Apple" } let fruitIsFrozen = fruit->Object.isFrozen // false ``` ## Specifications -- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) - [Object.isFrozen on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) +- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) */ @val external isFrozen: 'a => bool = "Object.isFrozen" From bfa541f24467f0aeed26b67f3593754bd08057d7 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Fri, 17 Mar 2023 00:18:56 -0700 Subject: [PATCH 12/34] better code examples --- src/Core__Object.res | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 45eda350..857dc8dc 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -23,19 +23,18 @@ @val external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" /** -`seal` seals an object. Sealing an object prevents extensions and makes existing properties non-configurable. A sealed object has a fixed set of properties. Unlike `freeze`, values of existing properties can still be changed as long as they are writable. `seal` returns the same object that was passed in. - -Any attempt to delete or add properties to a sealed object will fail, either silently or by throwing an error. +`seal` seals an object. Sealing an object prevents extensions and makes existing properties non-configurable. A sealed object has a fixed set of properties. Unlike `freeze`, values of existing properties can still be changed as long as they are writable. -Rescript usually [disallows modifying objects](https://rescript-lang.org/docs/manual/latest/object#update) regardless of whether they are sealed. +**Note:** `seal` returns the same object that was passed in; it does not create a copy. Any attempt to delete or add properties to a sealed object will fail, either silently or by throwing an error. ## Examples ```rescript -let point = {"x": 1, "y": 3}->Object.seal -let pointIsSealed = point->Object.isSealed // true -let fruit = {"name": "Apple" } -let fruitIsSealed = fruit->Object.isSealed // false +let point = {"x": 1, "y": 2} +point->Object.set("x", -7) // succeeds +point->Object.seal->ignore +point->Object.set("z", 9) // fails +point->Object.set("x", 13) // succeeds ``` ## Specifications - [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) From a34df2aa0bd1b32314ab272d090a233f04649301 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Fri, 17 Mar 2023 12:25:10 -0700 Subject: [PATCH 13/34] Object.get documentation and tests --- src/Core__Object.res | 16 +++++- test/ObjectTests.mjs | 119 +++++++++++++++++++++++++++++++++++++++++++ test/ObjectTests.res | 65 +++++++++++++++++++++++ test/TestSuite.mjs | 10 ++-- test/TestSuite.res | 1 + 5 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 test/ObjectTests.mjs create mode 100644 test/ObjectTests.res diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..c95f2848 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -11,7 +11,21 @@ @variadic @val external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" @val external copy: (@as(json`{}`) _, {..}) => {..} = "Object.assign" -@get_index external get: ({..}, string) => option<'a> = "" +/** +`get` gets the value of a property by name. Returns `None` if the property does not exist or has the value `undefined`. Otherwise returns `Some`, including if the value is `null`. + +## Examples + +```rescript +{"a": 1}->Object.get("a") // Some(1) +{"a": 1}->Object.get("b") // None +{"a": undefined}->Object.get("a") // None +{"a": null}->Object.get("a") // Some(null) +{"a": 1}->Object.get("toString")->Option.isSome // true +``` +*/ +@get_index +external get: ({..}, string) => option<'a> = "" @get_index external getSymbol: ({..}, Core__Symbol.t) => option<'a> = "" @get_index external getSymbolUnsafe: ({..}, Core__Symbol.t) => 'a = "" diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs new file mode 100644 index 00000000..3b21bb8c --- /dev/null +++ b/test/ObjectTests.mjs @@ -0,0 +1,119 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Test from "./Test.mjs"; +import * as Curry from "rescript/lib/es6/curry.js"; +import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; +import * as Core__Option from "../src/Core__Option.mjs"; + +var eq = Caml_obj.equal; + +function runGetTest(i) { + Test.run([ + [ + "ObjectTests.res", + 15, + 22, + 46 + ], + "Object.get: " + i.title + "" + ], Curry._1(i.get, Curry._1(i.source, undefined)), eq, i.expected); +} + +runGetTest({ + title: "prop exists, return Some", + source: (function (param) { + return { + a: 1 + }; + }), + get: (function (__x) { + return __x["a"]; + }), + expected: 1 + }); + +runGetTest({ + title: "prop NOT exist, return None", + source: (function (param) { + return { + a: 1 + }; + }), + get: (function (i) { + return i["banana"]; + }), + expected: undefined + }); + +runGetTest({ + title: "prop like toString, return Some", + source: (function (param) { + return { + a: 1 + }; + }), + get: (function (i) { + return Core__Option.isSome(i["toString"]); + }), + expected: true + }); + +runGetTest({ + title: "prop exist but explicitly undefined, return None", + source: (function (param) { + return { + a: undefined + }; + }), + get: (function (i) { + return i["a"]; + }), + expected: undefined + }); + +runGetTest({ + title: "prop exist but explicitly null, return None", + source: (function (param) { + return { + a: null + }; + }), + get: (function (i) { + return i["a"]; + }), + expected: null + }); + +runGetTest({ + title: "prop exists and is an array, can get it", + source: (function (param) { + return { + a: [ + 1, + 2, + 3 + ] + }; + }), + get: (function (i) { + return Core__Option.getWithDefault(Core__Option.map(i["a"], (function (i) { + return i.concat([ + 4, + 5 + ]); + })), []); + }), + expected: [ + 1, + 2, + 3, + 4, + 5 + ] + }); + +export { + eq , + runGetTest , +} +/* Not a pure module */ diff --git a/test/ObjectTests.res b/test/ObjectTests.res new file mode 100644 index 00000000..3ac95734 --- /dev/null +++ b/test/ObjectTests.res @@ -0,0 +1,65 @@ +open RescriptCore + +let eq = (a, b) => a == b + +// ===== get ===== + +type getTestData<'obj, 'res, 'expected> = { + title: string, + source: unit => 'obj, + get: 'obj => 'res, + expected: 'expected, +} + +let runGetTest = i => + Test.run(__POS_OF__(`Object.get: ${i.title}`), i.source()->i.get, eq, i.expected) + +{ + title: "prop exists, return Some", + source: () => {"a": 1}, + get: Object.get(_, "a"), + expected: Some(1), +}->runGetTest + +{ + title: "prop NOT exist, return None", + source: () => {"a": 1}, + get: i => i->Object.get("banana"), + expected: None, +}->runGetTest + +{ + title: "prop like toString, return Some", + source: () => {"a": 1}, + get: i => i->Object.get("toString")->Option.isSome, + expected: true, +}->runGetTest + +{ + title: "prop exist but explicitly undefined, return None", + source: () => {"a": undefined}, + get: i => i->Object.get("a"), + expected: None, +}->runGetTest + +{ + title: "prop exist but explicitly null, return None", + source: () => {"a": null}, + get: i => i->Object.get("a"), + expected: Some(null), +}->runGetTest + +{ + title: "prop exists and is an array, can get it", + source: () => {"a": [1, 2, 3]}, + get: i => i->Object.get("a")->Option.map(i => i->Array.concat([4, 5]))->Option.getWithDefault([]), + expected: [1, 2, 3, 4, 5], +}->runGetTest + +// This throws an exception +// { +// title: "prop exists but casted wrong on get", +// source: () => {"a": 34}, +// get: i => i->Object.get("a")->Option.map(i => i->Array.concat([4, 5]))->Option.getWithDefault([]), +// expected: [], +// }->runGetTest diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index c968bd8f..8caa6964 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -4,6 +4,7 @@ import * as IntTests from "./IntTests.mjs"; import * as TestTests from "./TestTests.mjs"; import * as ArrayTests from "./ArrayTests.mjs"; import * as ErrorTests from "./ErrorTests.mjs"; +import * as ObjectTests from "./ObjectTests.mjs"; import * as PromiseTest from "./PromiseTest.mjs"; var bign = TestTests.bign; @@ -26,10 +27,12 @@ var Concurrently = PromiseTest.Concurrently; var panicTest = ErrorTests.panicTest; -var eq = IntTests.eq; - var $$catch = IntTests.$$catch; +var eq = ObjectTests.eq; + +var runGetTest = ObjectTests.runGetTest; + export { bign , TestError , @@ -41,7 +44,8 @@ export { Catching , Concurrently , panicTest , - eq , $$catch , + eq , + runGetTest , } /* IntTests Not a pure module */ diff --git a/test/TestSuite.res b/test/TestSuite.res index 6277bf57..ca2a63ca 100644 --- a/test/TestSuite.res +++ b/test/TestSuite.res @@ -3,3 +3,4 @@ include PromiseTest include ErrorTests include ArrayTests include IntTests +include ObjectTests From 14ea9edadc965f18f91e3ed5a95065f6ccc6ddec Mon Sep 17 00:00:00 2001 From: jmagaram Date: Sun, 19 Mar 2023 20:17:12 -0700 Subject: [PATCH 14/34] Object.keysToArray documentation --- src/Core__Object.res | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..da7029db 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -18,7 +18,22 @@ @set_index external set: ({..}, string, 'a) => unit = "" @set_index external setSymbol: ({..}, Core__Symbol.t, 'a) => unit = "" -@val external keysToArray: {..} => array = "Object.keys" +/** +`keysToArray` returns an array of an object's own enumerable string-keyed property names. + +## Examples + +```rescript +{"a": 1, "b": 2}->Object.keysToArray // ["a", "b"] +{"a": undefined}->Object.keysToArray // ["a"] +Object.empty()->Object.keysToArray // [] +``` +## Specifications +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.keys) +- [Object.keys on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) +*/ +@val +external keysToArray: {..} => array = "Object.keys" @val external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" From 9a67b0c3447e825e15de2bff2b412af7c92c5dbf Mon Sep 17 00:00:00 2001 From: jmagaram Date: Mon, 20 Mar 2023 00:11:29 -0700 Subject: [PATCH 15/34] Object.assign documentation and tests --- src/Core__Object.res | 22 ++++++++++-- test/ObjectTests.mjs | 79 ++++++++++++++++++++++++++++++++++++++++++++ test/ObjectTests.res | 24 ++++++++++++++ test/TestSuite.mjs | 10 ++++-- test/TestSuite.res | 1 + 5 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 test/ObjectTests.mjs create mode 100644 test/ObjectTests.res diff --git a/src/Core__Object.res b/src/Core__Object.res index e2e14c50..5b4a20b5 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -7,8 +7,26 @@ @val external createWithNull: (@as(json`null`) _, unit) => {..} = "Object.create" @val external createWithNullAndProperties: (@as(json`null`) _, {..}) => {..} = "Object.create" -@val external assign: ({..}, {..}) => {..} = "Object.assign" -@variadic @val external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" +/** +`assign(target, source)` copies enumerable own properties from the source to the target, overwriting properties with the same name. It returns the modified target object. A deep clone is not created; properties are copied by reference. + +**Note:** ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object), including spreading one object into another. This is often more convenient than using `assign`. + +## Examples + +```rescript +Object.assign({"a": 1}, {"a": 2}) // {"a": 2} +Object.assign({"a": 1, "b": 2}, {"a": 0}) // {"a": 0, "b": 2} +Object.assign({"a": 1}, {"a": null}) // {"a": null} +``` +## Specifications +- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign) +- [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) +*/ +@val +external assign: ({..}, {..}) => {..} = "Object.assign" +@variadic @val +external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" @val external copy: (@as(json`{}`) _, {..}) => {..} = "Object.assign" @get_index external get: ({..}, string) => option<'a> = "" diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs new file mode 100644 index 00000000..a9d8efa3 --- /dev/null +++ b/test/ObjectTests.mjs @@ -0,0 +1,79 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Test from "./Test.mjs"; +import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; + +var eq = Caml_obj.equal; + +Test.run([ + [ + "ObjectTests.res", + 8, + 13, + 50 + ], + "assign copies from source to target" + ], Object.assign({ + a: 1, + b: 2 + }, { + b: 3, + c: 0 + }), eq, { + a: 1, + b: 3, + c: 0 + }); + +function assignOverwritesTarget(title, source) { + var sourceObj = { + a: source + }; + Test.run([ + [ + "ObjectTests.res", + 16, + 22, + 39 + ], + "assign " + title + "" + ], Object.assign({ + a: 1 + }, sourceObj), eq, sourceObj); + Test.run([ + [ + "ObjectTests.res", + 17, + 22, + 39 + ], + "assign " + title + "" + ], Object.assign({ + a: undefined + }, sourceObj), eq, sourceObj); + Test.run([ + [ + "ObjectTests.res", + 18, + 22, + 39 + ], + "assign " + title + "" + ], Object.assign({ + a: null + }, sourceObj), eq, sourceObj); +} + +assignOverwritesTarget("when source is undefined", undefined); + +assignOverwritesTarget("when source is null", null); + +assignOverwritesTarget("when source is a number", 1); + +assignOverwritesTarget("when source is a string", "abc"); + +export { + eq , + assignOverwritesTarget , +} +/* Not a pure module */ diff --git a/test/ObjectTests.res b/test/ObjectTests.res new file mode 100644 index 00000000..0b94a371 --- /dev/null +++ b/test/ObjectTests.res @@ -0,0 +1,24 @@ +open RescriptCore + +let eq = (a, b) => a == b + +// ====== assign ====== + +Test.run( + __POS_OF__("assign copies from source to target"), + Object.assign({"a": 1, "b": 2}, {"b": 3, "c": 0}), + eq, + {"a": 1, "b": 3, "c": 0}, +) + +let assignOverwritesTarget = (~title, ~source) => { + let sourceObj = {"a": source} + Test.run(__POS_OF__(`assign ${title}`), Object.assign({"a": 1}, sourceObj), eq, sourceObj) + Test.run(__POS_OF__(`assign ${title}`), Object.assign({"a": undefined}, sourceObj), eq, sourceObj) + Test.run(__POS_OF__(`assign ${title}`), Object.assign({"a": null}, sourceObj), eq, sourceObj) +} + +assignOverwritesTarget(~title="when source is undefined", ~source=undefined) +assignOverwritesTarget(~title="when source is null", ~source=null) +assignOverwritesTarget(~title="when source is a number", ~source=1) +assignOverwritesTarget(~title="when source is a string", ~source="abc") diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index c968bd8f..6f3c5c94 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -4,6 +4,7 @@ import * as IntTests from "./IntTests.mjs"; import * as TestTests from "./TestTests.mjs"; import * as ArrayTests from "./ArrayTests.mjs"; import * as ErrorTests from "./ErrorTests.mjs"; +import * as ObjectTests from "./ObjectTests.mjs"; import * as PromiseTest from "./PromiseTest.mjs"; var bign = TestTests.bign; @@ -26,10 +27,12 @@ var Concurrently = PromiseTest.Concurrently; var panicTest = ErrorTests.panicTest; -var eq = IntTests.eq; - var $$catch = IntTests.$$catch; +var eq = ObjectTests.eq; + +var assignOverwritesTarget = ObjectTests.assignOverwritesTarget; + export { bign , TestError , @@ -41,7 +44,8 @@ export { Catching , Concurrently , panicTest , - eq , $$catch , + eq , + assignOverwritesTarget , } /* IntTests Not a pure module */ diff --git a/test/TestSuite.res b/test/TestSuite.res index 6277bf57..ca2a63ca 100644 --- a/test/TestSuite.res +++ b/test/TestSuite.res @@ -3,3 +3,4 @@ include PromiseTest include ErrorTests include ArrayTests include IntTests +include ObjectTests From b4b3fda93acc2a2186feb7a35ffbca62db075ab0 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Mon, 20 Mar 2023 12:41:59 -0700 Subject: [PATCH 16/34] getSymbol docs and tests --- src/Core__Object.res | 15 ++++++++++++++- test/ObjectTests.mjs | 29 +++++++++++++++++++++++++++++ test/ObjectTests.res | 23 +++++++++++++++++++++++ test/TestSuite.mjs | 3 +++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index c95f2848..c02eef1e 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -26,7 +26,20 @@ */ @get_index external get: ({..}, string) => option<'a> = "" -@get_index external getSymbol: ({..}, Core__Symbol.t) => option<'a> = "" +/** +`getSymbol` gets the value of a property by symbol. Returns `None` if the property does not exist or has the value `undefined`. Otherwise returns `Some`, including if the value is `null`. + +## Examples + +```rescript +let fruit = Symbol.make("fruit") +let x = Object.empty() +x->Object.setSymbol(fruit, "banana") +x->Object.getSymbol(fruit) // Some("banana") +``` +*/ +@get_index +external getSymbol: ({..}, Core__Symbol.t) => option<'a> = "" @get_index external getSymbolUnsafe: ({..}, Core__Symbol.t) => 'a = "" @set_index external set: ({..}, string, 'a) => unit = "" diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs index 3b21bb8c..fe718d1b 100644 --- a/test/ObjectTests.mjs +++ b/test/ObjectTests.mjs @@ -112,8 +112,37 @@ runGetTest({ ] }); +function getSymbolTestWhenExists(param) { + var obj = {}; + var fruit = Symbol("fruit"); + obj[fruit] = "banana"; + var retrieved = obj[fruit]; + Test.run([ + [ + "ObjectTests.res", + 75, + 15, + 63 + ], + "Object.getSymbol when exists return it as Some" + ], retrieved, eq, "banana"); +} + +getSymbolTestWhenExists(undefined); + +Test.run([ + [ + "ObjectTests.res", + 84, + 13, + 65 + ], + "Object.getSymbol when not exists return it as None" + ], ({})[Symbol("fruit")], eq, undefined); + export { eq , runGetTest , + getSymbolTestWhenExists , } /* Not a pure module */ diff --git a/test/ObjectTests.res b/test/ObjectTests.res index 3ac95734..27a99d7a 100644 --- a/test/ObjectTests.res +++ b/test/ObjectTests.res @@ -63,3 +63,26 @@ let runGetTest = i => // get: i => i->Object.get("a")->Option.map(i => i->Array.concat([4, 5]))->Option.getWithDefault([]), // expected: [], // }->runGetTest + +// ===== getSymbol ===== + +let getSymbolTestWhenExists = () => { + let obj = Object.empty() + let fruit = Symbol.make("fruit") + obj->Object.setSymbol(fruit, "banana") + let retrieved = obj->Object.getSymbol(fruit) + Test.run( + __POS_OF__(`Object.getSymbol when exists return it as Some`), + retrieved, + eq, + Some("banana"), + ) +} +getSymbolTestWhenExists() + +Test.run( + __POS_OF__(`Object.getSymbol when not exists return it as None`), + Object.empty()->Object.getSymbol(Symbol.make("fruit")), + eq, + None, +) diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index 8caa6964..80ecf948 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -33,6 +33,8 @@ var eq = ObjectTests.eq; var runGetTest = ObjectTests.runGetTest; +var getSymbolTestWhenExists = ObjectTests.getSymbolTestWhenExists; + export { bign , TestError , @@ -47,5 +49,6 @@ export { $$catch , eq , runGetTest , + getSymbolTestWhenExists , } /* IntTests Not a pure module */ From 2b52a040763c1034d0ffe473af2957e0088f9265 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 10:05:46 -0700 Subject: [PATCH 17/34] change undefined to None --- src/Core__Object.res | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index da7029db..7c11e3f2 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -25,8 +25,7 @@ ```rescript {"a": 1, "b": 2}->Object.keysToArray // ["a", "b"] -{"a": undefined}->Object.keysToArray // ["a"] -Object.empty()->Object.keysToArray // [] +{"a": None}->Object.keysToArray // ["a"]Object.empty()->Object.keysToArray // [] ``` ## Specifications - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.keys) From 8856175839b822e16c6677277af09547040385bc Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 10:24:56 -0700 Subject: [PATCH 18/34] no Specifications header in docs --- src/Core__Object.res | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 7c11e3f2..2ed21c54 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -19,17 +19,16 @@ @set_index external setSymbol: ({..}, Core__Symbol.t, 'a) => unit = "" /** -`keysToArray` returns an array of an object's own enumerable string-keyed property names. +`keysToArray` returns an array of an object's own enumerable string-keyed property names. See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.keys) +or [Object.keys on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys). ## Examples ```rescript {"a": 1, "b": 2}->Object.keysToArray // ["a", "b"] -{"a": None}->Object.keysToArray // ["a"]Object.empty()->Object.keysToArray // [] +{"a": None}->Object.keysToArray // ["a"] +Object.empty()->Object.keysToArray // [] ``` -## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.keys) -- [Object.keys on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) */ @val external keysToArray: {..} => array = "Object.keys" From a251145caa31678c976796a0d60cacbe753d807a Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 10:35:50 -0700 Subject: [PATCH 19/34] remove Specifications section. docs for assignMany but no examples. --- src/Core__Object.res | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 5b4a20b5..432927ea 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -12,6 +12,8 @@ **Note:** ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object), including spreading one object into another. This is often more convenient than using `assign`. +See [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) or [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign). + ## Examples ```rescript @@ -19,13 +21,18 @@ Object.assign({"a": 1}, {"a": 2}) // {"a": 2} Object.assign({"a": 1, "b": 2}, {"a": 0}) // {"a": 0, "b": 2} Object.assign({"a": 1}, {"a": null}) // {"a": null} ``` -## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign) -- [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) */ @val external assign: ({..}, {..}) => {..} = "Object.assign" -@variadic @val +@variadic +@val +/** +`assignMany(target, sources)` copies enumerable own properties from each source to the target, overwriting properties with the same name. Later sources' properties overwrite earlier ones. It returns the modified target object. A deep clone is not created; properties are copied by reference. + +**Note:** ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object), including spreading one object into another. This is often more convenient than using `assign` or `assignMany`. + +See [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) or [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign). +*/ external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" @val external copy: (@as(json`{}`) _, {..}) => {..} = "Object.assign" From a37c1b810a68aa8ce484d41854114200f8a98dce Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 10:56:46 -0700 Subject: [PATCH 20/34] remove Specifications section in docs --- src/Core__Object.res | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 1e95ef89..351db51f 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -26,6 +26,8 @@ /** `preventExtensions` prevents new properties from being added to the object. It modifies the object (rather than creating a copy) and returns it. +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.preventextensions) and [Object.preventExtensions on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) + ## Examples ```rescript @@ -34,9 +36,6 @@ obj->Object.set("b", 2) // succeeds obj->Object.preventExtensions->ignore obj->Object.set("c", 3) // fails ``` -## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.preventextensions) -- [Object.preventExtensions on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) */ @val external preventExtensions: 'a => 'a = "Object.preventExtensions" @@ -45,19 +44,18 @@ external preventExtensions: 'a => 'a = "Object.preventExtensions" @val external isSealed: 'a => bool = "Object.isSealed" @val external isFrozen: 'a => bool = "Object.isFrozen" /** - `isExtensible` determines if an object is extensible (whether it can have new properties added to it). +`isExtensible` determines if an object is extensible (whether it can have new properties added to it). + +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isextensible) and [Object.isExtensible on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) - ## Examples +## Examples - ```rescript +```rescript let obj = {"a": 1} obj->Object.isExtensible // true obj->Object.preventExtensions->ignore obj->Object.isExtensible // false ``` -## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isextensible) -- [Object.isExtensible on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) */ @val external isExtensible: 'a => bool = "Object.isExtensible" From b37ce6b7b17b76d6c4d982f64d1addea2d11a97a Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 11:02:19 -0700 Subject: [PATCH 21/34] remove Specifications section --- src/Core__Object.res | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 857dc8dc..36f23f29 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -27,6 +27,8 @@ **Note:** `seal` returns the same object that was passed in; it does not create a copy. Any attempt to delete or add properties to a sealed object will fail, either silently or by throwing an error. +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.seal) and [Object.seal on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) + ## Examples ```rescript @@ -36,10 +38,6 @@ point->Object.seal->ignore point->Object.set("z", 9) // fails point->Object.set("x", 13) // succeeds ``` -## Specifications -- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.seal) -- [Object.seal on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) */ @val external seal: 'a => 'a = "Object.seal" @@ -47,21 +45,19 @@ external seal: 'a => 'a = "Object.seal" @val external freeze: 'a => 'a = "Object.freeze" /** - `isSealed` determines if an object is sealed. A sealed object has a fixed set of properties. - - ## Examples - - ```rescript - let point = {"x": 1, "y": 3}->Object.seal - let pointIsSealed = point->Object.isSealed // true - let fruit = {"name": "Apple" } - let fruitIsSealed = fruit->Object.isSealed // false - ``` - ## Specifications - - [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - - [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.issealed) - - [Object.isSealed on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) - */ +`isSealed` determines if an object is sealed. A sealed object has a fixed set of properties. + +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.issealed) and [Object.isSealed on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) + +## Examples + +```rescript +let point = {"x": 1, "y": 3}->Object.seal +let pointIsSealed = point->Object.isSealed // true +let fruit = {"name": "Apple" } +let fruitIsSealed = fruit->Object.isSealed // false + ``` +*/ @val external isSealed: 'a => bool = "Object.isSealed" @val external isFrozen: 'a => bool = "Object.isFrozen" From 319e3b59a2f901fac0b7818c6da07060748f2a95 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 11:06:32 -0700 Subject: [PATCH 22/34] remove Specifications section --- src/Core__Object.res | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 2bc2f5a2..753453c5 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -27,21 +27,19 @@ /** `freeze` freezes an object. Freezing an object makes existing properties non-writable and prevents extensions. Once an object is frozen, new properties cannot be be added, existing properties cannot be removed, and their values cannot be changed. -**Note:** `freeze` returns the same object that was passed in; it does not create a frozen copy. Any attempt to change a frozen object will fail, either silently or by throwing an exception. +**Note:** `freeze` returns the same object that was passed in; it does not create a frozen copy. Any attempt to change a frozen object will fail, either silently or by throwing an exception. - ## Examples +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) and [Object.isFrozen on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen). + +## Examples ```rescript let obj = {"a": 1} obj->Object.set("a", 2) // succeeds obj->Object.freeze->ignore obj->Object.set("a", 3) // fails - ``` - ## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.freeze) -- [Object.freeze on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) -- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - */ +``` +*/ @val external freeze: 'a => 'a = "Object.freeze" @@ -49,6 +47,8 @@ external freeze: 'a => 'a = "Object.freeze" /** `isFrozen` determines if an object is frozen. An object is frozen if an only if it is not extensible, all its properties are non-configurable, and all its data properties are non-writable. +See [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) and [Object.isFrozen on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen). + ## Examples ```rescript @@ -57,11 +57,7 @@ let pointIsFrozen = point->Object.isFrozen // true let fruit = {"name": "Apple" } let fruitIsFrozen = fruit->Object.isFrozen // false ``` - ## Specifications -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.isfrozen) -- [Object.isFrozen on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) -- [Updating objects in Rescript](https://rescript-lang.org/docs/manual/latest/object#update) - */ +*/ @val external isFrozen: 'a => bool = "Object.isFrozen" @val external isExtensible: 'a => bool = "Object.isExtensible" From 2d0fb2c3728fbdfa458d947c6684a4183d5f3cc2 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 11:11:54 -0700 Subject: [PATCH 23/34] remove Specifications section --- src/Core__Object.res | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 811840cb..38cb4ac9 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,13 +1,13 @@ @obj external empty: unit => {..} = "" /** -`is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. +`is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. See [Object.is on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) -**Note:** In most scenarios use `==` or `===` or the custom `equals` function (if provided) for the type. +In most scenarios use `==` or `===` or the custom `equals` function (if provided) for the type. The `==` operator [is different in ReScript than Javascript](https://rescript-lang.org/docs/manual/latest/overview#boolean). Arrays, records and other non-primitives are equal if they have the same contents (deep equality). -In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar but not identical to `Object.is`; see [Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). +In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar but not identical to `is` ## Examples @@ -29,11 +29,6 @@ Object.is(fruit, {"name": "Apple" }) // false fruit == {"name": "Apple" } // true fruit === {"name": "Apple" } // false ``` -## Specifications - -- [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.is) -- [`Object.is on MDN`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) -- [Equality operators in ReScript](https://rescript-lang.org/docs/manual/latest/overview#boolean) */ @val external is: ('a, 'b) => bool = "Object.is" From 3bca6782387854bf61e2ac690216a47105803e9f Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 12:45:04 -0700 Subject: [PATCH 24/34] remove some of the docs based on Glenn's feedback --- src/Core__Object.res | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 38cb4ac9..02326974 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -5,10 +5,6 @@ In most scenarios use `==` or `===` or the custom `equals` function (if provided) for the type. -The `==` operator [is different in ReScript than Javascript](https://rescript-lang.org/docs/manual/latest/overview#boolean). Arrays, records and other non-primitives are equal if they have the same contents (deep equality). - -In ReScript, the `===` operator performs a strict equality check, like Javascript, and is similar but not identical to `is` - ## Examples ```rescript From 5dec67f77cf4ddd371920b985f6827e81b702a06 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 13:46:21 -0700 Subject: [PATCH 25/34] stricted comparison types glennsl feedback --- src/Core__Object.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 02326974..a30a3acd 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -27,7 +27,7 @@ fruit === {"name": "Apple" } // false ``` */ @val -external is: ('a, 'b) => bool = "Object.is" +external is: ('a, 'a) => bool = "Object.is" @val external create: {..} => {..} = "Object.create" @val external createWithProperties: ({..}, {..}) => {..} = "Object.create" From 08db9a8c8c1c1f11bf8b1558bd7285c49e3c7e67 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 13:50:52 -0700 Subject: [PATCH 26/34] stricter types on object.is, must be same test updates --- test/ObjectTests.mjs | 30 ------------------------------ test/ObjectTests.res | 6 +++--- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs index 0d1cdd8a..d9145984 100644 --- a/test/ObjectTests.mjs +++ b/test/ObjectTests.mjs @@ -5,16 +5,6 @@ import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; var eq = Caml_obj.equal; -Test.run([ - [ - "ObjectTests.res", - 7, - 20, - 41 - ], - "is: different types" - ], Object.is("abc", false), eq, false); - Test.run([ [ "ObjectTests.res", @@ -75,26 +65,6 @@ Test.run([ "is: null and undefined" ], Object.is(null, null), eq, true); -Test.run([ - [ - "ObjectTests.res", - 17, - 20, - 44 - ], - "is: null and undefined" - ], Object.is(null, undefined), eq, false); - -Test.run([ - [ - "ObjectTests.res", - 18, - 20, - 44 - ], - "is: undefined and None" - ], Object.is(undefined, undefined), eq, true); - var nums = [ 1, 2, diff --git a/test/ObjectTests.res b/test/ObjectTests.res index ad6551ac..5c1a4b8d 100644 --- a/test/ObjectTests.res +++ b/test/ObjectTests.res @@ -4,7 +4,7 @@ let eq = (a, b) => a == b // ===== is ===== -Test.run(__POS_OF__("is: different types"), Object.is("abc", false), eq, false) +// Test.run(__POS_OF__("is: different types"), Object.is("abc", false), eq, false) Test.run(__POS_OF__("is: ints"), Object.is(25, 25), eq, true) @@ -14,8 +14,8 @@ Test.run(__POS_OF__("is: strings"), Object.is("abc", "ABC"), eq, false) Test.run(__POS_OF__("is: null and undefined"), Object.is(null, undefined), eq, false) Test.run(__POS_OF__("is: null and undefined"), Object.is(undefined, undefined), eq, true) Test.run(__POS_OF__("is: null and undefined"), Object.is(null, null), eq, true) -Test.run(__POS_OF__("is: null and undefined"), Object.is(null, None), eq, false) -Test.run(__POS_OF__("is: undefined and None"), Object.is(undefined, None), eq, true) // hmmm... +// Test.run(__POS_OF__("is: null and undefined"), Object.is(null, None), eq, false) +// Test.run(__POS_OF__("is: undefined and None"), Object.is(undefined, None), eq, true) let nums = [1, 2, 3] Test.run(__POS_OF__("is: arrays"), Object.is([1, 2, 3], [1, 2, 3]), eq, false) From 6539e1b3205f71920c3870ae97fa93df5fb01be6 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Tue, 21 Mar 2023 23:38:28 -0700 Subject: [PATCH 27/34] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f580aa..f5204cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## main +### API changes + +- Add `Result.forEach` https://github.com/rescript-association/rescript-core/pull/116 + +### Documentation + +- Docstrings for `Object`. Not yet complete. https://github.com/rescript-association/rescript-core/pull/117 + ## 0.2.0 ### API changes From d7601b9cd99c0c0e756950c621b6a2fd6a65ac58 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 00:50:15 -0700 Subject: [PATCH 28/34] docs for Object.empty --- src/Core__Object.res | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 6835b57e..c39daa52 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -1,4 +1,16 @@ -@obj external empty: unit => {..} = "" +/** +`empty` create a new object that inherits the properties and methods from the standard built-in Object, such as `toString`. See [Object on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) + +## Examples + +```rescript +let x = Object.empty() +x->Object.keysToArray->Array.length // 0 +x->Object.get("toString")->Option.isSome // true +``` +*/ +@obj +external empty: unit => {..} = "" /** `is` determines if two objects are identical in all contexts. Objects, arrays, records, and other non-primitives are only identical if they reference the **exact** same object in memory. Primitives like ints, floats, and strings are identical if they have the same value. `+0` and `-0` are distinct. NaN is equal to itself. See [Object.is on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) From 6375876a8a9eb8317b843c526fe6cb6822d7f3be Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 01:11:12 -0700 Subject: [PATCH 29/34] Object.set documentation --- src/Core__Object.res | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index c39daa52..a1d7662d 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -109,7 +109,20 @@ x->Object.getSymbol(fruit) // Some("banana") external getSymbol: ({..}, Core__Symbol.t) => option<'a> = "" @get_index external getSymbolUnsafe: ({..}, Core__Symbol.t) => 'a = "" -@set_index external set: ({..}, string, 'a) => unit = "" +/** +`set(name, value)` assigns a value to the named object property, overwriting the previous value if any. See [Working with Objects on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#objects_and_properties) + +## Examples + +```rescript +{"a": 1}->Object.set("a", 2) // {"a": 2} +{"a": 1}->Object.set("a", None) // {"a": None} +{"a": 1}->Object.set("b", 2) // {"a": 1, "b": 2} +``` +*/ +@set_index +external set: ({..}, string, 'a) => unit = "" + @set_index external setSymbol: ({..}, Core__Symbol.t, 'a) => unit = "" /** From 6a59ec237b1f2eaa62d89ae10db3f78beb99296f Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 09:42:49 -0700 Subject: [PATCH 30/34] safer type signatures {..} as 'a returns 'a --- src/Core__Object.res | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index a1d7662d..0f80569f 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -75,7 +75,7 @@ See [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScri */ external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" -@val external copy: (@as(json`{}`) _, {..}) => {..} = "Object.assign" +@val external copy: (@as(json`{}`) _, {..} as 'a) => 'a = "Object.assign" /** `get` gets the value of a property by name. Returns `None` if the property does not exist or has the value `undefined`. Otherwise returns `Some`, including if the value is `null`. @@ -160,7 +160,7 @@ point->Object.set("x", 13) // succeeds ``` */ @val -external seal: 'a => 'a = "Object.seal" +external seal: ({..} as 'a) => 'a = "Object.seal" /** `preventExtensions` prevents new properties from being added to the object. It modifies the object (rather than creating a copy) and returns it. @@ -177,7 +177,7 @@ obj->Object.set("c", 3) // fails ``` */ @val -external preventExtensions: 'a => 'a = "Object.preventExtensions" +external preventExtensions: ({..} as 'a) => 'a = "Object.preventExtensions" /** `freeze` freezes an object. Freezing an object makes existing properties non-writable and prevents extensions. Once an object is frozen, new properties cannot be be added, existing properties cannot be removed, and their values cannot be changed. @@ -196,7 +196,7 @@ obj->Object.set("a", 3) // fails ``` */ @val -external freeze: 'a => 'a = "Object.freeze" +external freeze: ({..} as 'a) => 'a = "Object.freeze" /** `isSealed` determines if an object is sealed. A sealed object has a fixed set of properties. From 16bbe60a359899fe0a70dddba8a0a179f0686904 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 10:05:37 -0700 Subject: [PATCH 31/34] warning on Object.assign --- src/Core__Object.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 0f80569f..fa59a243 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -49,7 +49,7 @@ external is: ('a, 'a) => bool = "Object.is" /** `assign(target, source)` copies enumerable own properties from the source to the target, overwriting properties with the same name. It returns the modified target object. A deep clone is not created; properties are copied by reference. -**Note:** ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object), including spreading one object into another. This is often more convenient than using `assign`. +**Warning:** ReScript provides compile-time support for type-safe access to JavaScript objects. This eliminates common errors such as accessing properties that do not exist, or using a property of type x as if it were a y. Using `assign` can bypass these safety checks and lead to run-time errors (if you are not careful). ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object) and [records](https://rescript-lang.org/docs/manual/latest/record). This is often safer and more convenient than using `assign` and other functions in this module. See [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) or [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign). From 1e2e296227f5ff07caaeec824114eeb1bb7b1971 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 10:15:55 -0700 Subject: [PATCH 32/34] hasOwnProperty docs --- src/Core__Object.res | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index fa59a243..3d93427c 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -140,7 +140,20 @@ Object.empty()->Object.keysToArray // [] @val external keysToArray: {..} => array = "Object.keys" -@val external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" +/** +`hasOwnProperty` determines whether the object has the specified property as its **own** property, as opposed to inheriting it. See [hasOwnProperty on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) + +## Examples + +```rescript +let point = {"x": 1, "y": 2} +{"a": 1}->hasOwnProperty("a") // true +{"a": 1}->hasOwnProperty("b") // false +{"a": 1}->hasOwnProperty("toString") // false +``` +*/ +@val +external hasOwnProperty: ({..}, string) => bool = "Object.prototype.hasOwnProperty.call" /** `seal` seals an object. Sealing an object prevents extensions and makes existing properties non-configurable. A sealed object has a fixed set of properties. Unlike `freeze`, values of existing properties can still be changed as long as they are writable. From edb17985961490e78a3959590eb9c3991147caf1 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Thu, 23 Mar 2023 11:22:41 -0700 Subject: [PATCH 33/34] Object.create docs --- src/Core__Object.res | 20 ++++++++++++++++++-- test/ObjectTests.mjs | 24 ++++++++++++++++++++++++ test/ObjectTests.res | 17 +++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index 3d93427c..af83a19e 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -41,8 +41,24 @@ fruit === {"name": "Apple" } // false @val external is: ('a, 'a) => bool = "Object.is" -@val external create: {..} => {..} = "Object.create" -@val external createWithProperties: ({..}, {..}) => {..} = "Object.create" +/** +`create` creates a new object, using an existing object as the prototype of the new object. See [Object.create on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) + +**Note:** ReScript provides [first-class support for immutable objects](https://rescript-lang.org/docs/manual/latest/object) and [records](https://rescript-lang.org/docs/manual/latest/record). This is often safer and more convenient than using `create` and other functions in this module. + +## Examples + +```rescript +let x = {"fruit": "banana"} +let y = Object.create(x) +y->Object.get("fruit") // Some("banana") +``` +*/ +@val +external create: {..} => {..} = "Object.create" + +@val +external createWithProperties: ({..}, {..}) => {..} = "Object.create" @val external createWithNull: (@as(json`null`) _, unit) => {..} = "Object.create" @val external createWithNullAndProperties: (@as(json`null`) _, {..}) => {..} = "Object.create" diff --git a/test/ObjectTests.mjs b/test/ObjectTests.mjs index c180d955..5b2ac5cd 100644 --- a/test/ObjectTests.mjs +++ b/test/ObjectTests.mjs @@ -599,6 +599,30 @@ Test.run([ "Object.getSymbol when not exists return it as None" ], ({})[Symbol("fruit")], eq, undefined); +Test.run([ + [ + "ObjectTests.res", + 168, + 13, + 46 + ], + "Object.create clones properties" + ], Object.create({ + a: 1 + })["a"], eq, 1); + +Test.run([ + [ + "ObjectTests.res", + 175, + 13, + 46 + ], + "Object.create clones properties" + ], Object.create({ + a: 1 + })["b"], eq, undefined); + export { eq , nums , diff --git a/test/ObjectTests.res b/test/ObjectTests.res index c0605598..6f888696 100644 --- a/test/ObjectTests.res +++ b/test/ObjectTests.res @@ -161,3 +161,20 @@ Test.run( eq, None, ) + +// ===== create ===== + +Test.run( + __POS_OF__(`Object.create clones properties`), + {"a": 1}->Object.create->Object.get("a"), + eq, + Some(1), +) + +Test.run( + __POS_OF__(`Object.create clones properties`), + {"a": 1}->Object.create->Object.get("b"), + eq, + None, +) + From 5d2f8751a5f2dbb2fe5a315cc444ac73663c7490 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Sun, 16 Jul 2023 16:09:13 -0700 Subject: [PATCH 34/34] add spacing between method names --- src/Core__Object.res | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Core__Object.res b/src/Core__Object.res index af83a19e..b40a5a15 100644 --- a/src/Core__Object.res +++ b/src/Core__Object.res @@ -57,9 +57,10 @@ y->Object.get("fruit") // Some("banana") @val external create: {..} => {..} = "Object.create" -@val -external createWithProperties: ({..}, {..}) => {..} = "Object.create" +@val external createWithProperties: ({..}, {..}) => {..} = "Object.create" + @val external createWithNull: (@as(json`null`) _, unit) => {..} = "Object.create" + @val external createWithNullAndProperties: (@as(json`null`) _, {..}) => {..} = "Object.create" /** @@ -80,8 +81,6 @@ Object.assign({"a": 1}, {"a": null}) // {"a": null} @val external assign: ({..}, {..}) => {..} = "Object.assign" -@variadic -@val /** `assignMany(target, sources)` copies enumerable own properties from each source to the target, overwriting properties with the same name. Later sources' properties overwrite earlier ones. It returns the modified target object. A deep clone is not created; properties are copied by reference. @@ -89,6 +88,8 @@ external assign: ({..}, {..}) => {..} = "Object.assign" See [Object.assign on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) or [ECMAScript Language Specification](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign). */ +@variadic +@val external assignMany: ({..}, array<{..}>) => {..} = "Object.assign" @val external copy: (@as(json`{}`) _, {..} as 'a) => 'a = "Object.assign" @@ -123,6 +124,7 @@ x->Object.getSymbol(fruit) // Some("banana") */ @get_index external getSymbol: ({..}, Core__Symbol.t) => option<'a> = "" + @get_index external getSymbolUnsafe: ({..}, Core__Symbol.t) => 'a = "" /**