Repository: char0n/ramda-adjunct (js)

Issue:
isSafeInteger
Determines whether the provided value is a number that is a safe integer.

Complete description of what safe integer is, can be found here on [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger). The linked page also contains polyfill and preferred implementation of the `isSafeInteger` function. Modern javascript already contains function `Number.isSafeInteger`.

Expected behavior
```js
RA.isSafeInteger(3);                    // true
RA.isSafeInteger(Math.pow(2, 53));      // false
RA.isSafeInteger(Math.pow(2, 53) - 1);  // true
RA.isSafeInteger(NaN);                  // false
RA.isSafeInteger(Infinity);             // false
RA.isSafeInteger('3');                  // false
RA.isSafeInteger(3.1);                  // false
RA.isSafeInteger(3.0);                  // true
```

[This PR](https://github.com/char0n/ramda-adjunct/pull/1067) should serve as a model template of PR for `isSafeInteger` function. 


Pull request:
feat(type): add  isSafeInteger
Closes #747

Patch:
diff --git a/package-lock.json b/package-lock.json
index a087acee3f..4d4d060768 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2674,9 +2674,9 @@
       },
       "dependencies": {
         "bluebird": {
-          "version": "3.7.0",
-          "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz",
-          "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==",
+          "version": "3.7.1",
+          "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
+          "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
           "dev": true
         },
         "rimraf": {
@@ -4821,9 +4821,9 @@
       }
     },
     "eslint-plugin-mocha": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.1.1.tgz",
-      "integrity": "sha512-p/otruG425jRYDa28HjbBYYXoFNzq3Qp++gn5dbE44Kz4NvmIsSUKSV1T+RLYUcZOcdJKKAftXbaqkHFqReKoA==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.0.tgz",
+      "integrity": "sha512-vE/+tHJVom2BkMOiwkOKcAM5YqGPk3C6gMvQ32DHihKkaXF6vmxtj3UEOg64wP3m8/Zk5V/UmQbFE5nqu1EXSg==",
       "dev": true,
       "requires": {
         "ramda": "^0.26.1"
@@ -5538,9 +5538,9 @@
       },
       "dependencies": {
         "is-buffer": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
-          "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+          "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
           "dev": true
         }
       }
@@ -7108,20 +7108,20 @@
       }
     },
     "husky": {
-      "version": "3.0.8",
-      "resolved": "https://registry.npmjs.org/husky/-/husky-3.0.8.tgz",
-      "integrity": "sha512-HFOsgcyrX3qe/rBuqyTt+P4Gxn5P0seJmr215LAZ/vnwK3jWB3r0ck7swbzGRUbufCf9w/lgHPVbF/YXQALgfQ==",
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-3.0.9.tgz",
+      "integrity": "sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg==",
       "dev": true,
       "requires": {
         "chalk": "^2.4.2",
+        "ci-info": "^2.0.0",
         "cosmiconfig": "^5.2.1",
         "execa": "^1.0.0",
         "get-stdin": "^7.0.0",
-        "is-ci": "^2.0.0",
         "opencollective-postinstall": "^2.0.2",
         "pkg-dir": "^4.2.0",
         "please-upgrade-node": "^3.2.0",
-        "read-pkg": "^5.1.1",
+        "read-pkg": "^5.2.0",
         "run-node": "^1.0.0",
         "slash": "^3.0.0"
       },
@@ -7544,15 +7544,6 @@
       "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
       "dev": true
     },
-    "is-ci": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
-      "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
-      "dev": true,
-      "requires": {
-        "ci-info": "^2.0.0"
-      }
-    },
     "is-data-descriptor": {
       "version": "0.1.4",
       "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -9353,9 +9344,9 @@
       }
     },
     "mocha": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.1.tgz",
-      "integrity": "sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A==",
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz",
+      "integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==",
       "dev": true,
       "requires": {
         "ansi-colors": "3.2.3",
@@ -12170,9 +12161,9 @@
       }
     },
     "terser": {
-      "version": "4.3.8",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.8.tgz",
-      "integrity": "sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ==",
+      "version": "4.3.9",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
+      "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
       "dev": true,
       "requires": {
         "commander": "^2.20.0",
@@ -13074,9 +13065,9 @@
       }
     },
     "webpack": {
-      "version": "4.41.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.1.tgz",
-      "integrity": "sha512-ak7u4tUu/U63sCVxA571IuPZO/Q0pZ9cEXKg+R/woxkDzVovq57uB6L2Hlg/pC8LCU+TWpvtcYwsstivQwMJmw==",
+      "version": "4.41.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz",
+      "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==",
       "dev": true,
       "requires": {
         "@webassemblyjs/ast": "1.8.5",
diff --git a/package.json b/package.json
index fb9ebf6b78..f5cb42c716 100644
--- a/package.json
+++ b/package.json
@@ -149,20 +149,20 @@
     "eslint-config-airbnb-base": "=14.0.0",
     "eslint-config-prettier": "6.4.0",
     "eslint-plugin-import": "2.18.2",
-    "eslint-plugin-mocha": "6.1.1",
+    "eslint-plugin-mocha": "6.2.0",
     "eslint-plugin-prettier": "3.1.1",
     "eslint-plugin-ramda": "2.5.1",
     "fantasy-land": "4.0.0",
     "fantasy-laws": "=1.2.0",
     "folktale": "=2.3.2",
     "glob": "7.1.4",
-    "husky": "3.0.8",
+    "husky": "3.0.9",
     "istanbul": "=0.4.5",
     "jsdoc": "3.6.3",
     "jsverify": "0.8.4",
     "license-cli": "1.1.6",
     "lint-staged": "9.4.2",
-    "mocha": "6.2.1",
+    "mocha": "6.2.2",
     "mocha-junit-reporter": "1.23.1",
     "mocha-multi-reporters": "1.1.7",
     "monet": "0.9.0",
@@ -177,7 +177,7 @@
     "testem": "3.0.0",
     "tslint": "=5.14.0",
     "typescript": "=3.5.3",
-    "webpack": "4.41.1",
+    "webpack": "4.41.2",
     "webpack-cli": "3.3.9"
   },
   "browserslist": "> 0.25%, ie 10, ie 11, not op_mini all",
diff --git a/src/allUnique.js b/src/allUnique.js
new file mode 100644
index 0000000000..63337363bb
--- /dev/null
+++ b/src/allUnique.js
@@ -0,0 +1,25 @@
+import { converge, length, uniq } from 'ramda';
+
+import lengthEq from './lengthEq';
+
+/**
+ * Returns true if all items in the list are unique. `R.equals` is used to determine equality.
+ *
+ * @func allUnique
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category List
+ * @sig [a] -> Boolean
+ * @param {Array} list The list of values
+ * @return {boolean}
+ * @see {@link https://ramdajs.com/docs/#equals|equals}
+ * @example
+ *
+ * RA.allUnique([ 1, 2, 3, 4 ]); //=> true
+ * RA.allUnique([ 1, 1, 2, 3 ]); //=> false
+ * RA.allUnique([]); //=> true
+ *
+ */
+const allUnique = converge(lengthEq, [length, uniq]);
+
+export default allUnique;
diff --git a/src/index.js b/src/index.js
index 9a3f6b4ab7..a3dc558547 100644
--- a/src/index.js
+++ b/src/index.js
@@ -49,9 +49,12 @@ export { default as isNumber } from './isNumber';
 export { default as isNotNumber } from './isNotNumber';
 export { default as isPositive } from './isPositive';
 export { default as isNegative } from './isNegative';
+export { default as isPositiveZero } from './isPositiveZero';
+export { default as isNegativeZero } from './isNegativeZero';
 export { default as isNotNilOrEmpty } from './isNotNilOrEmpty';
 export { default as isNonPositive } from './isNonPositive';
 export { default as isNonNegative } from './isNonNegative';
+export { default as isMap } from './isMap';
 export { default as isNaN } from './isNaN';
 export { default as isNotNaN } from './isNotNaN';
 export { default as isFinite } from './isFinite';
@@ -74,8 +77,10 @@ export { default as isTruthy } from './isTruthy';
 export { default as isFalsy } from './isFalsy';
 export { default as isRegExp } from './isRegExp';
 export { default as isNotRegExp } from './isNotRegExp';
+export { default as isSet } from './isSet';
 export { default as isSparseArray } from './isSparseArray';
 export { default as isSymbol } from './isSymbol';
+export { default as isSafeInteger } from './isSafeInteger';
 // Function
 export { default as stubUndefined } from './stubUndefined';
 export { default as stubNull } from './stubNull';
@@ -135,6 +140,7 @@ export { default as allIdenticalTo } from './allIdenticalTo';
 export { default as allEqualTo } from './allEqualTo';
 export { default as flattenDepth } from './flattenDepth';
 export { default as toArray } from './toArray';
+export { default as allUnique } from './allUnique';
 // Object
 export { default as paths } from './paths';
 export { default as renameKeys } from './renameKeys';
diff --git a/src/internal/polyfills/Number.isSafeInteger.js b/src/internal/polyfills/Number.isSafeInteger.js
new file mode 100644
index 0000000000..82b2d39760
--- /dev/null
+++ b/src/internal/polyfills/Number.isSafeInteger.js
@@ -0,0 +1,11 @@
+import { both } from 'ramda';
+
+import isInteger from '../../isInteger';
+import MAX_SAFE_INTEGER from './Number.MAX_SAFE_INTEGER';
+
+const isSafeIntegerPolyfill = both(
+  isInteger,
+  value => Math.abs(value) <= MAX_SAFE_INTEGER
+);
+
+export default isSafeIntegerPolyfill;
diff --git a/src/isMap.js b/src/isMap.js
new file mode 100644
index 0000000000..78e6807546
--- /dev/null
+++ b/src/isMap.js
@@ -0,0 +1,31 @@
+import { type, identical, pipe } from 'ramda';
+import curry1 from 'ramda/src/internal/_curry1';
+
+/**
+ * Predicate for determining if a provided value is an instance of a Map.
+ *
+ * @func isMap
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Type
+ * @sig * -> Boolean
+ * @param {*} val The value to test
+ * @return {boolean}
+ * @see {@link RA.isSet|isSet}}
+ * @example
+ *
+ * RA.isMap(new Map()); //=> true
+ * RA.isMap(new Map([[1, 2], [2, 1]])); //=> true
+ * RA.isSet(new Set()); //=> false
+ * RA.isSet(new Set([1,2]); //=> false
+ * RA.isSet(new Object()); //=> false
+ */
+
+const isMap = curry1(
+  pipe(
+    type,
+    identical('Map')
+  )
+);
+
+export default isMap;
diff --git a/src/isNegativeZero.js b/src/isNegativeZero.js
new file mode 100644
index 0000000000..e0518a0f43
--- /dev/null
+++ b/src/isNegativeZero.js
@@ -0,0 +1,22 @@
+import { identical } from 'ramda';
+
+/**
+ * Checks if value is a negative zero (-0).
+ *
+ * @func isNegativeZero
+ * @memberof RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Type
+ * @sig * -> Boolean
+ * @param {*} val The value to test
+ * @return {boolean}
+ * @see @see {@link RA.isPositiveZero|isPositiveZero}
+ * @example
+ *
+ * RA.isNegativeZero(-0); //=> true
+ * RA.isNegativeZero(+0); //=> false
+ * RA.isNegativeZero(null); //=> false
+ */
+const isNegativeZero = identical(-0);
+
+export default isNegativeZero;
diff --git a/src/isPositiveZero.js b/src/isPositiveZero.js
new file mode 100644
index 0000000000..b0b1f96240
--- /dev/null
+++ b/src/isPositiveZero.js
@@ -0,0 +1,23 @@
+import { identical } from 'ramda';
+
+/**
+ * Checks if value is a positive zero (+0).
+ *
+ * @func isPositiveZero
+ * @memberof RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Type
+ * @sig * -> Boolean
+ * @param {*} val The value to test
+ * @return {boolean}
+ * @see {@link RA.isNegativeZero|isNegativeZero}
+ * @example
+ *
+ * RA.isPositiveZero(+0); //=> true
+ * RA.isPositiveZero(0); //=> true
+ * RA.isPositiveZero(-0); //=> false
+ * RA.isPositiveZero(null); //=> false
+ */
+const isPositiveZero = identical(+0);
+
+export default isPositiveZero;
diff --git a/src/isSafeInteger.js b/src/isSafeInteger.js
new file mode 100644
index 0000000000..4bd4fc4e03
--- /dev/null
+++ b/src/isSafeInteger.js
@@ -0,0 +1,41 @@
+import { bind } from 'ramda';
+import curry1 from 'ramda/src/internal/_curry1';
+
+import isFunction from './isFunction';
+import polyfill from './internal/polyfills/Number.isSafeInteger';
+
+export const isSafeIntegerPolyfill = curry1(polyfill);
+
+/**
+ * Checks whether the passed value is a safe `integer`.
+ *
+ * @func isSafeInteger
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Type
+ * @sig * -> Boolean
+ * @param {*} val The value to test
+ * @return {boolean}
+ * @example
+ *
+ * RA.isSafeInteger(3); //=> true
+ * RA.isSafeInteger(Math.pow(2, 53)) //=> false
+ * RA.isSafeInteger(Math.pow(2, 53) - 1); //=> true
+ * RA.isSafeInteger(NaN); //=> false
+ * RA.isSafeInteger(Infinity); //=> false
+ * RA.isSafeInteger('3') //=> false
+ * RA.isSafeInteger(3.1); //=> false
+ * RA.isSafeInteger(3.0); //=> true
+ * RA.isSafeInteger('string'); //=> false
+ * RA.isSafeInteger(null); //=> false
+ * RA.isSafeInteger(undefined); //=> false
+ * RA.isSafeInteger({}); //=> false
+ * RA.isSafeInteger(() => { }); //=> false
+ * RA.isSafeInteger(true); //=> false
+ */
+
+const isSafeInteger = isFunction(Number.isSafeInteger)
+  ? curry1(bind(Number.isSafeInteger, Number))
+  : isSafeIntegerPolyfill;
+
+export default isSafeInteger;
diff --git a/src/isSet.js b/src/isSet.js
new file mode 100644
index 0000000000..a45a668a13
--- /dev/null
+++ b/src/isSet.js
@@ -0,0 +1,30 @@
+import { type, identical, pipe } from 'ramda';
+import curry1 from 'ramda/src/internal/_curry1';
+
+/**
+ * Predicate for determining if a provided value is an instance of a Set.
+ *
+ * @func isSet
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Type
+ * @sig * -> Boolean
+ * @param {*} val The value to test
+ * @return {boolean}
+ * @see {@link RA.isMap|isMap}}
+ * @example
+ *
+ * RA.isSet(new Map()); //=> false
+ * RA.isSet(new Set()); //=> true
+ * RA.isSet(new Set([1,2]); //=> true
+ * RA.isSet(new Object()); //=> false
+ */
+
+const isSet = curry1(
+  pipe(
+    type,
+    identical('Set')
+  )
+);
+
+export default isSet;
diff --git a/types/index.d.ts b/types/index.d.ts
index cb46ee5521..3498f901f6 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -245,6 +245,11 @@ declare namespace RamdaAdjunct {
          */
         isInvalidDate(val: any): boolean; // alias of isNotValidDate
 
+        /**
+         * Checks if value is `Map`.
+         */
+        isMap(val: any): val is Map<any, any>;
+
         /**
          * Checks whether the passed value is `NaN` and its type is `Number`.
          * It is a more robust version of the original, global isNaN().
@@ -278,6 +283,16 @@ declare namespace RamdaAdjunct {
          */
         isNegative(val: any): val is number;
 
+        /**
+         * Checks if value is a positive zero (+0).
+         */
+        isPositiveZero(val: any): boolean;
+
+        /**
+         * Checks if value is a negative zero (-0).
+         */
+        isNegativeZero(val: any): boolean;
+
         /**
          * Checks if value is a non-positive `Number` primitive or object. This includes all
          * negative numbers and zero.
@@ -315,6 +330,11 @@ declare namespace RamdaAdjunct {
          */
         isFloat(val: any): val is number;
 
+        /**
+         * Checks whether the passed value is a safe `integer`.
+         */
+        isSafeInteger(val: any): boolean;
+
         /**
          * Checks whether the passed value is complement of a `float`.
          */
@@ -362,6 +382,11 @@ declare namespace RamdaAdjunct {
          */
         isRegExp(val: any): boolean;
 
+        /**
+         * Checks if value is `Set`.
+         */
+        isSet(val: any): val is Set<any>;
+
         /**
          * Checks if value is complement of `RegExp` object.
          */
@@ -1211,6 +1236,11 @@ declare namespace RamdaAdjunct {
          * Converts value to an array.
          */
         toArray<T>(iterable: Iterable<T> | T): any[];
+
+        /**
+         * Returns true if all items in the list are unique. `R.equals` is used to determine equality.
+         */
+        allUnique<T>(list: T[]): boolean;
     }
 }
 


Test patch:
diff --git a/test/allUnique.js b/test/allUnique.js
new file mode 100644
index 0000000000..19882fbf87
--- /dev/null
+++ b/test/allUnique.js
@@ -0,0 +1,37 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+
+describe('allUnique', function() {
+  context('given an array of unique elements', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.allUnique([1, 2, 3, 4, 5, 6]));
+      assert.isTrue(RA.allUnique(['a', 'b', 'c']));
+      assert.isTrue(RA.allUnique([{}, { a: 1 }, { b: 2 }]));
+      assert.isTrue(RA.allUnique([null, undefined, '', 0]));
+    });
+  });
+
+  context('given an array of not unique elements', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.allUnique([1, 2, 3, 4, 5, 1]));
+      assert.isFalse(RA.allUnique(['b', 'b', 'c']));
+      assert.isFalse(RA.allUnique([{}, {}]));
+      assert.isFalse(RA.allUnique([[1], [1]]));
+      assert.isFalse(RA.allUnique([undefined, undefined, undefined]));
+    });
+  });
+
+  context('given an empty array', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.allUnique([]));
+    });
+  });
+
+  specify('should support placeholder to specify "gaps"', function() {
+    const allUnique = RA.allUnique(R.__);
+
+    assert.isTrue(allUnique([1, 2, 3, 4, 5, 6]));
+  });
+});
diff --git a/test/isMap.js b/test/isMap.js
new file mode 100644
index 0000000000..f74894e740
--- /dev/null
+++ b/test/isMap.js
@@ -0,0 +1,37 @@
+import * as R from 'ramda';
+import { assert } from 'chai';
+
+import * as RA from '../src';
+import Symbol from './shared/Symbol';
+
+describe('isMap', function() {
+  context('given a map', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.isMap(new Map()));
+      assert.isTrue(RA.isMap(new Map([[1, 2], [2, 1]])));
+    });
+  });
+
+  context('given not a map', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.isMap('abc'));
+      assert.isFalse(RA.isMap(Object('abc')));
+      assert.isFalse(RA.isMap(Object()));
+      assert.isFalse(RA.isMap(new Date()));
+      assert.isFalse(RA.isMap(new Error()));
+      assert.isFalse(RA.isMap(RA.isMap));
+      assert.isFalse(RA.isMap(3));
+      assert.isFalse(RA.isMap(Symbol));
+      assert.isFalse(RA.isMap(R));
+      assert.isFalse(RA.isMap(new Set()));
+      assert.isFalse(RA.isMap(new Set([1, 2])));
+      assert.isFalse(RA.isMap(/regex/));
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', function() {
+    const isMap = RA.isMap(R.__);
+
+    assert.isTrue(isMap(new Map()));
+  });
+});
diff --git a/test/isNegativeZero.js b/test/isNegativeZero.js
new file mode 100644
index 0000000000..c142d1bcbf
--- /dev/null
+++ b/test/isNegativeZero.js
@@ -0,0 +1,38 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+
+describe('isNegativeZero', function() {
+  context('given a negative zero (-0)', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.isNegativeZero(-0));
+    });
+  });
+
+  context('given a value different from negative zero (-0)', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.isNegativeZero(+0));
+      assert.isFalse(RA.isNegativeZero(null));
+      assert.isFalse(RA.isNegativeZero(true));
+      assert.isFalse(RA.isNegativeZero(NaN));
+      assert.isFalse(RA.isNegativeZero({}));
+      assert.isFalse(RA.isNegativeZero([]));
+      assert.isFalse(RA.isNegativeZero(() => {}));
+      assert.isFalse(RA.isNegativeZero('string'));
+      assert.isFalse(RA.isNegativeZero(1));
+      assert.isFalse(RA.isNegativeZero(0.1));
+      assert.isFalse(RA.isNegativeZero(Object(0.1)));
+      assert.isFalse(RA.isNegativeZero(NaN));
+      assert.isFalse(RA.isNegativeZero(Infinity));
+      assert.isFalse(RA.isNegativeZero(Number.MAX_VALUE));
+      assert.isFalse(RA.isNegativeZero(Number.MIN_VALUE));
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', function() {
+    const isNegativeZero = RA.isNegativeZero(R.__);
+
+    assert.isTrue(isNegativeZero(-0));
+  });
+});
diff --git a/test/isPositiveZero.js b/test/isPositiveZero.js
new file mode 100644
index 0000000000..e88d6aea31
--- /dev/null
+++ b/test/isPositiveZero.js
@@ -0,0 +1,39 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+
+describe('isPositiveZero', function() {
+  context('given a positive zero (+0) or (0)', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.isPositiveZero(+0));
+      assert.isTrue(RA.isPositiveZero(0));
+    });
+  });
+
+  context('given a value different from positive zero (+0)', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.isPositiveZero(-0));
+      assert.isFalse(RA.isPositiveZero(null));
+      assert.isFalse(RA.isPositiveZero(true));
+      assert.isFalse(RA.isPositiveZero(NaN));
+      assert.isFalse(RA.isPositiveZero({}));
+      assert.isFalse(RA.isPositiveZero([]));
+      assert.isFalse(RA.isPositiveZero(() => {}));
+      assert.isFalse(RA.isPositiveZero('string'));
+      assert.isFalse(RA.isPositiveZero(1));
+      assert.isFalse(RA.isPositiveZero(0.1));
+      assert.isFalse(RA.isPositiveZero(Object(0.1)));
+      assert.isFalse(RA.isPositiveZero(NaN));
+      assert.isFalse(RA.isPositiveZero(Infinity));
+      assert.isFalse(RA.isPositiveZero(Number.MAX_VALUE));
+      assert.isFalse(RA.isPositiveZero(Number.MIN_VALUE));
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', function() {
+    const isPositiveZero = RA.isPositiveZero(R.__);
+
+    assert.isTrue(isPositiveZero(+0));
+  });
+});
diff --git a/test/isSafeInteger.js b/test/isSafeInteger.js
new file mode 100644
index 0000000000..a33b9a436a
--- /dev/null
+++ b/test/isSafeInteger.js
@@ -0,0 +1,70 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+import { isSafeIntegerPolyfill } from '../src/isSafeInteger';
+import MAX_SAFE_INTEGER from '../src/internal/polyfills/Number.MAX_SAFE_INTEGER';
+
+describe('isSafeInteger', function() {
+  context('given a safe integer', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.isSafeInteger(3));
+      assert.isTrue(RA.isSafeInteger(MAX_SAFE_INTEGER));
+      assert.isTrue(RA.isSafeInteger(3.0));
+    });
+  });
+
+  context('given not safe integer', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.isSafeInteger(MAX_SAFE_INTEGER + 1));
+      assert.isFalse(RA.isSafeInteger(NaN));
+      assert.isFalse(RA.isSafeInteger(Infinity));
+      assert.isFalse(RA.isSafeInteger('3'));
+      assert.isFalse(RA.isSafeInteger(3.1));
+      assert.isFalse(RA.isSafeInteger('string'));
+      assert.isFalse(RA.isSafeInteger(null));
+      assert.isFalse(RA.isSafeInteger(undefined));
+      assert.isFalse(RA.isSafeInteger({}));
+      assert.isFalse(RA.isSafeInteger(() => {}));
+      assert.isFalse(RA.isSafeInteger(true));
+    });
+  });
+
+  specify('should support placeholder to specify "gaps"', function() {
+    const isSafeInteger = RA.isSafeInteger(R.__);
+
+    assert.isTrue(isSafeInteger(3));
+  });
+
+  context('fromPolyfill', function() {
+    context('given a safe integer', function() {
+      specify('should return true', function() {
+        assert.isTrue(isSafeIntegerPolyfill(3));
+        assert.isTrue(isSafeIntegerPolyfill(MAX_SAFE_INTEGER));
+        assert.isTrue(isSafeIntegerPolyfill(3.0));
+      });
+    });
+
+    context('given not safe integer', function() {
+      specify('should return false', function() {
+        assert.isFalse(isSafeIntegerPolyfill(MAX_SAFE_INTEGER + 1));
+        assert.isFalse(isSafeIntegerPolyfill(NaN));
+        assert.isFalse(isSafeIntegerPolyfill(Infinity));
+        assert.isFalse(isSafeIntegerPolyfill('3'));
+        assert.isFalse(isSafeIntegerPolyfill(3.1));
+        assert.isFalse(isSafeIntegerPolyfill('string'));
+        assert.isFalse(isSafeIntegerPolyfill(null));
+        assert.isFalse(isSafeIntegerPolyfill(undefined));
+        assert.isFalse(isSafeIntegerPolyfill({}));
+        assert.isFalse(isSafeIntegerPolyfill(() => {}));
+        assert.isFalse(isSafeIntegerPolyfill(true));
+      });
+    });
+
+    specify('should support placeholder to specify "gaps"', function() {
+      const isSafeInteger = isSafeIntegerPolyfill(R.__);
+
+      assert.isTrue(isSafeInteger(3));
+    });
+  });
+});
diff --git a/test/isSet.js b/test/isSet.js
new file mode 100644
index 0000000000..4b3d305175
--- /dev/null
+++ b/test/isSet.js
@@ -0,0 +1,34 @@
+import * as R from 'ramda';
+import { assert } from 'chai';
+
+import * as RA from '../src';
+import Symbol from './shared/Symbol';
+
+describe('isSet', function() {
+  context('given a set', function() {
+    specify('should return true', function() {
+      assert.isTrue(RA.isSet(new Set()));
+      assert.isTrue(RA.isSet(new Set([1, 2])));
+    });
+  });
+
+  context('given not a set', function() {
+    specify('should return false', function() {
+      assert.isFalse(RA.isSet('abc'));
+      assert.isFalse(RA.isSet(Object('abc')));
+      assert.isFalse(RA.isSet(new Date()));
+      assert.isFalse(RA.isSet(new Error()));
+      assert.isFalse(RA.isSet(RA.isSet));
+      assert.isFalse(RA.isSet(3));
+      assert.isFalse(RA.isSet(Symbol));
+      assert.isFalse(RA.isSet(R));
+      assert.isFalse(RA.isSet(/regex/));
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', function() {
+    const isSet = RA.isSet(R.__);
+
+    assert.isTrue(isSet(new Set()));
+  });
+});
diff --git a/types/test/allUnique.ts b/types/test/allUnique.ts
new file mode 100644
index 0000000000..750c973ce4
--- /dev/null
+++ b/types/test/allUnique.ts
@@ -0,0 +1,12 @@
+import * as RA from 'ramda-adjunct';
+
+import { customIterable } from './helpers';
+
+RA.allUnique([1, 2, 3, 4]); // $ExpectType boolean
+RA.allUnique(['a', 'b', 'c', 'd']); // $ExpectType boolean
+RA.allUnique([1, 'b', 2, 'd']); // $ExpectType boolean
+RA.allUnique([]); // $ExpectType boolean
+
+RA.allUnique(1); // $ExpectError
+RA.allUnique({}); // $ExpectError
+RA.allUnique(customIterable); // $ExpectError
diff --git a/types/test/isMap.ts b/types/test/isMap.ts
new file mode 100644
index 0000000000..ab25304d0d
--- /dev/null
+++ b/types/test/isMap.ts
@@ -0,0 +1,17 @@
+import * as RA from 'ramda-adjunct';
+
+RA.isMap(new Map()); // $ExpectType boolean
+RA.isMap(new Map([[1, 2], [2, 1]])); // $ExpectType boolean
+RA.isMap(new Set()); // $ExpectType boolean
+RA.isMap(new Set([1, 2])); // $ExpectType boolean
+RA.isMap(+0); // $ExpectType boolean
+RA.isMap(null); // $ExpectType boolean
+RA.isMap(true); // $ExpectType boolean
+RA.isMap(NaN); // $ExpectType boolean
+RA.isMap({}); // $ExpectType boolean
+RA.isMap([]); // $ExpectType boolean
+RA.isMap(() => {}); // $ExpectType boolean
+RA.isMap('string'); // $ExpectType boolean
+RA.isMap(1); // $ExpectType boolean
+RA.isMap(Object()); // $ExpectType boolean
+RA.isMap(Object(5)); // $ExpectType boolean
diff --git a/types/test/isNegativeZero.ts b/types/test/isNegativeZero.ts
new file mode 100644
index 0000000000..380faaad2d
--- /dev/null
+++ b/types/test/isNegativeZero.ts
@@ -0,0 +1,18 @@
+import * as RA from 'ramda-adjunct';
+
+RA.isNegativeZero(-0); // $ExpectType boolean
+RA.isNegativeZero(+0); // $ExpectType boolean
+RA.isNegativeZero(null); // $ExpectType boolean
+RA.isNegativeZero(true); // $ExpectType boolean
+RA.isNegativeZero(NaN); // $ExpectType boolean
+RA.isNegativeZero({}); // $ExpectType boolean
+RA.isNegativeZero([]); // $ExpectType boolean
+RA.isNegativeZero(() => {}); // $ExpectType boolean
+RA.isNegativeZero('string'); // $ExpectType boolean
+RA.isNegativeZero(1); // $ExpectType boolean
+RA.isNegativeZero(0.1); // $ExpectType boolean
+RA.isNegativeZero(Object(0.1)); // $ExpectType boolean
+RA.isNegativeZero(NaN); // $ExpectType boolean
+RA.isNegativeZero(Infinity); // $ExpectType boolean
+RA.isNegativeZero(Number.MAX_VALUE); // $ExpectType boolean
+RA.isNegativeZero(Number.MIN_VALUE); // $ExpectType boolean
diff --git a/types/test/isPositiveZero.ts b/types/test/isPositiveZero.ts
new file mode 100644
index 0000000000..04f5122f21
--- /dev/null
+++ b/types/test/isPositiveZero.ts
@@ -0,0 +1,18 @@
+import * as RA from 'ramda-adjunct';
+
+RA.isPositiveZero(+0); // $ExpectType boolean
+RA.isPositiveZero(-0); // $ExpectType boolean
+RA.isPositiveZero(null); // $ExpectType boolean
+RA.isPositiveZero(true); // $ExpectType boolean
+RA.isPositiveZero(NaN); // $ExpectType boolean
+RA.isPositiveZero({}); // $ExpectType boolean
+RA.isPositiveZero([]); // $ExpectType boolean
+RA.isPositiveZero(() => {}); // $ExpectType boolean
+RA.isPositiveZero('string'); // $ExpectType boolean
+RA.isPositiveZero(1); // $ExpectType boolean
+RA.isPositiveZero(0.1); // $ExpectType boolean
+RA.isPositiveZero(Object(0.1)); // $ExpectType boolean
+RA.isPositiveZero(NaN); // $ExpectType boolean
+RA.isPositiveZero(Infinity); // $ExpectType boolean
+RA.isPositiveZero(Number.MAX_VALUE); // $ExpectType boolean
+RA.isPositiveZero(Number.MIN_VALUE); // $ExpectType boolean
diff --git a/types/test/isSafeInteger.ts b/types/test/isSafeInteger.ts
new file mode 100644
index 0000000000..0482e774d2
--- /dev/null
+++ b/types/test/isSafeInteger.ts
@@ -0,0 +1,16 @@
+import * as RA from 'ramda-adjunct';
+
+RA.isSafeInteger(3); // $ExpectType boolean
+RA.isSafeInteger(Math.pow(2, 53)); // $ExpectType boolean
+RA.isSafeInteger(Math.pow(2, 53) - 1); // $ExpectType boolean
+RA.isSafeInteger(NaN); // $ExpectType boolean
+RA.isSafeInteger(Infinity); // $ExpectType boolean
+RA.isSafeInteger('3'); // $ExpectType boolean
+RA.isSafeInteger(3.1); // $ExpectType boolean
+RA.isSafeInteger(3.0); // $ExpectType boolean
+RA.isSafeInteger('string'); // $ExpectType boolean
+RA.isSafeInteger(null); // $ExpectType boolean
+RA.isSafeInteger(undefined); // $ExpectType boolean
+RA.isSafeInteger({}); // $ExpectType boolean
+RA.isSafeInteger(() => { }); // $ExpectType boolean
+RA.isSafeInteger(true); // $ExpectType boolean
diff --git a/types/test/isSet.ts b/types/test/isSet.ts
new file mode 100644
index 0000000000..80e3cb3300
--- /dev/null
+++ b/types/test/isSet.ts
@@ -0,0 +1,16 @@
+import * as RA from 'ramda-adjunct';
+
+RA.isSet(new Set()); // $ExpectType boolean
+RA.isSet(new Set([1, 2])); // $ExpectType boolean
+RA.isSet(+0); // $ExpectType boolean
+RA.isSet(null); // $ExpectType boolean
+RA.isSet(true); // $ExpectType boolean
+RA.isSet(NaN); // $ExpectType boolean
+RA.isSet({}); // $ExpectType boolean
+RA.isSet([]); // $ExpectType boolean
+RA.isSet(() => {}); // $ExpectType boolean
+RA.isSet('string'); // $ExpectType boolean
+RA.isSet(1); // $ExpectType boolean
+RA.isSet(Object()); // $ExpectType boolean
+RA.isSet(Object(5)); // $ExpectType boolean
+RA.isSet(new Map()); // $ExpectType boolean