From a159a3a0660a2da9a8ff6392aba4b2de81bc8391 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 6 Aug 2019 13:27:55 -0700 Subject: [PATCH] [Breaking] boxed primitives are unwrapped for comparisons --- index.js | 12 +++++++++++- package.json | 2 ++ test/cmp.js | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 35dcdb3..ff30525 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,8 @@ var isRegex = require('is-regex'); var flags = require('regexp.prototype.flags'); var isArray = require('isarray'); var isDate = require('is-date-object'); +var isBoxedPrimitive = require('is-boxed-primitive'); +var toPrimitive = require('es-to-primitive/es2015'); // TODO: replace this with ES2020 once updated var getTime = Date.prototype.getTime; var gPO = Object.getPrototypeOf; @@ -18,6 +20,12 @@ function deepEqual(actual, expected, options) { return true; } + var actualBoxed = isBoxedPrimitive(actual); + var expectedBoxed = isBoxedPrimitive(expected); + if (actualBoxed || expectedBoxed) { + return deepEqual(toPrimitive(actual), toPrimitive(expected), opts); + } + // 7.3. Other pairs that do not both pass typeof value == 'object', equivalence is determined by ==. if (!actual || !expected || (typeof actual !== 'object' && typeof expected !== 'object')) { return opts.strict ? is(actual, expected) : actual == expected; @@ -53,8 +61,9 @@ function isBuffer(x) { } function objEquiv(a, b, opts) { - /* eslint max-statements: [2, 70] */ + /* eslint max-statements: [2, 70], max-lines-per-function: [2, 80] */ var i, key; + if (typeof a !== typeof b) { return false; } if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { return false; } @@ -121,6 +130,7 @@ function objEquiv(a, b, opts) { for (i = ka.length - 1; i >= 0; i--) { if (ka[i] != kb[i]) { return false; } } + // equivalent values for every corresponding key, and ~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; diff --git a/package.json b/package.json index 8055cc1..89dbe23 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "test": "npm run tests-only" }, "dependencies": { + "es-to-primitive": "^1.2.0", "is-arguments": "^1.0.4", + "is-boxed-primitive": "^1.0.0", "is-date-object": "^1.0.1", "is-regex": "^1.0.4", "isarray": "^2.0.5", diff --git a/test/cmp.js b/test/cmp.js index 49ad67a..4d6871f 100644 --- a/test/cmp.js +++ b/test/cmp.js @@ -419,3 +419,27 @@ test('toStringTag', { skip: !hasSymbols || !Symbol.toStringTag }, function (t) { t.end(); }); + +test('boxed primitives', function (t) { + t.deepEqualTest(Object(false), false, 'boxed and primitive `false`', true, true); + t.deepEqualTest(Object(true), true, 'boxed and primitive `true`', true, true); + t.deepEqualTest(Object(3), 3, 'boxed and primitive `3`', true, true); + t.deepEqualTest(Object(NaN), NaN, 'boxed and primitive `NaN`', false, true); + t.deepEqualTest(Object(''), '', 'boxed and primitive `""`', true, true); + t.deepEqualTest(Object('str'), 'str', 'boxed and primitive `"str"`', true, true); + + t.test('symbol', { skip: !hasSymbols }, function (st) { + var s = Symbol(''); + st.deepEqualTest(Object(s), s, 'boxed and primitive `Symbol()`', true, true); + st.end(); + }); + + /* globals BigInt: false */ + t.test('bigint', { skip: typeof BigInt !== 'function' }, function (st) { + var hhgtg = BigInt(42); + st.deepEqualTest(Object(hhgtg), hhgtg, 'boxed and primitive `BigInt(42)`', true, true); + st.end(); + }); + + t.end(); +});