Skip to content

Commit

Permalink
feat: set/is and set/ensure utils
Browse files Browse the repository at this point in the history
  • Loading branch information
medikoo committed Mar 8, 2021
1 parent 6fda8ce commit 083ec23
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -120,6 +120,9 @@ Each `*/ensure` utility, accepts following options (eventually passed with secon
- **Iterable**
- [`iterable/is`](docs/iterable.md#iterableis)
- [`iterable/ensure`](docs/iterable.md#iterableensure)
- **Set**
- [`set/is`](docs/set.md#setis)
- [`set/ensure`](docs/set.md#setensure)
- **Map**
- [`map/is`](docs/map.md#mapis)
- [`map/ensure`](docs/map.md#mapensure)
Expand Down
27 changes: 27 additions & 0 deletions docs/set.md
@@ -0,0 +1,27 @@
# Set

_Set_ instance

## `set/is`

Confirms if given object is a native set\_

```javascript
const isSet = require("type/set/is");

isSet(new Set()); // true
isSet(new Set()); // false
isSet({}); // false
```

## `Set/ensure`

If given argument is a _set_, it is returned back. Otherwise `TypeError` is thrown.

```javascript
const ensureSet = require("type/set/ensure");

const set = new Set();
ensureSet(set); // set
eensureSet({}); // Thrown TypeError: [object Object] is not a set
```
12 changes: 12 additions & 0 deletions set/ensure.js
@@ -0,0 +1,12 @@
"use strict";

var resolveException = require("../lib/resolve-exception")
, is = require("./is");

module.exports = function (value /*, options*/) {
if (is(value)) return value;
var options = arguments[1];
var errorMessage =
options && options.name ? "Expected a set for %n, received %v" : "%v is not a set";
return resolveException(value, errorMessage, options);
};
27 changes: 27 additions & 0 deletions set/is.js
@@ -0,0 +1,27 @@
"use strict";

var isPrototype = require("../prototype/is");

// In theory we could rely on Symbol.toStringTag directly,
// still early native implementation (e.g. in FF) predated symbols
var objectToString = Object.prototype.toString, objectTaggedString = objectToString.call(new Set());

module.exports = function (value) {
if (!value) return false;

// Sanity check (reject objects which do not expose common Promise interface)
try {
if (typeof value.add !== "function") return false;
if (typeof value.has !== "function") return false;
if (typeof value.clear !== "function") return false;
} catch (error) {
return false;
}

// Ensure its native Promise object (has [[SetData]] slot)
// Note: it's not 100% precise as string tag may be overriden
// and other objects could be hacked to expose it
if (objectToString.call(value) !== objectTaggedString) return false;

return !isPrototype(value);
};
29 changes: 29 additions & 0 deletions test/set/ensure.js
@@ -0,0 +1,29 @@
"use strict";

var assert = require("chai").assert
, ensureSet = require("../../set/ensure");

describe("set/ensure", function () {
it("Should return input value", function () {
var value = new Set();
assert.equal(ensureSet(value), value);
});
it("Should crash on no value", function () {
try {
ensureSet({});
throw new Error("Unexpected");
} catch (error) {
assert.equal(error.name, "TypeError");
assert.equal(error.message, "[object Object] is not a set");
}
});
it("Should provide alternative error message when name option is passed", function () {
try {
ensureSet(null, { name: "name" });
throw new Error("Unexpected");
} catch (error) {
assert.equal(error.name, "TypeError");
assert.equal(error.message, "Expected a set for name, received null");
}
});
});
47 changes: 47 additions & 0 deletions test/set/is.js
@@ -0,0 +1,47 @@
"use strict";

var assert = require("chai").assert
, isSet = require("../../set/is");

describe("set/is", function () {
if (typeof Set === "function") {
it("Should return true on set", function () { assert.equal(isSet(new Set()), true); });
}
it("Should return false on object that mimicks map", function () {
assert.equal(
isSet({
add: function () { /* noop */ },
has: function () { /* noop */ },
clear: function () { /* noop */ }
}),
false
);
});

it("Should return false on plain object", function () { assert.equal(isSet({}), false); });
it("Should return false on function", function () {
assert.equal(isSet(function () { return true; }), false);
});
it("Should return false on array", function () { assert.equal(isSet([]), false); });
if (typeof Object.create === "function") {
it("Should return false on object with no prototype", function () {
assert.equal(isSet(Object.create(null)), false);
});
}
it("Should return false on string", function () { assert.equal(isSet("foo"), false); });
it("Should return false on empty string", function () { assert.equal(isSet(""), false); });
it("Should return false on number", function () { assert.equal(isSet(123), false); });
it("Should return false on NaN", function () { assert.equal(isSet(NaN), false); });
it("Should return false on boolean", function () { assert.equal(isSet(true), false); });
if (typeof Symbol === "function") {
it("Should return false on symbol", function () {
assert.equal(isSet(Symbol("foo")), false);
});
}
if (typeof Map === "function") {
it("Should return false on map", function () { assert.equal(isSet(new Map()), false); });
}

it("Should return false on null", function () { assert.equal(isSet(null), false); });
it("Should return false on undefined", function () { assert.equal(isSet(void 0), false); });
});

0 comments on commit 083ec23

Please sign in to comment.