Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@types/jest": "^24.0.23",
"@typescript-eslint/eslint-plugin": "^2.11.0",
"@typescript-eslint/parser": "^2.11.0",
"dequal": "^2.0.3",
"eslint": "^6.8.0",
"eslint-plugin-jest": "^23.1.1",
"eslint-plugin-json": "^2.0.1",
Expand Down
108 changes: 88 additions & 20 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { dequal } from "dequal";
import { validArrayValue } from "../arrays";
import { shallowEqual, shallowEqualArrays } from "../index";
import { eq, shallowEqual, shallowEqualArrays } from "../index";
import shallowEqualObjects, { validObjectValue } from "../objects";

const arr = [1, 2, 3];
Expand Down Expand Up @@ -117,34 +118,101 @@ const objTests: {
},
];

describe("shallowEqual on arrays", () => {
arrTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqual(test.arrA, test.arrB)).toEqual(test.result);
describe("shallow-equal", () => {
describe("shallowEqual on arrays", () => {
arrTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqual(test.arrA, test.arrB)).toEqual(test.result);
});
});
});
});

describe("shallowEqual on objects", () => {
objTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqual(test.objA, test.objB)).toEqual(test.result);
describe("shallowEqual on objects", () => {
objTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqual(test.objA, test.objB)).toEqual(test.result);
});
});
});
});

describe("shallowEqualObjects", () => {
objTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqualObjects(test.objA, test.objB)).toEqual(test.result);
describe("shallowEqualObjects", () => {
objTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqualObjects(test.objA, test.objB)).toEqual(test.result);
});
});
});
});

describe("shallowEqualArrays", () => {
arrTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqualArrays(test.arrA, test.arrB)).toEqual(test.result);
describe("shallowEqualArrays", () => {
arrTests.forEach((test) => {
it("should " + test.should, () => {
expect(shallowEqualArrays(test.arrA, test.arrB)).toEqual(test.result);
});
});
});

describe("custom comparators", () => {
it("should use a custom comparator", () => {
const comparator = jest.fn((a: any, b: any) => a === b);
const arrA = [1, 2, 3];
const arrB = [1, 2, 3];

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqual(arrA, arrB, comparator)).toEqual(true);
expect(comparator).toHaveBeenCalledTimes(3);

const badComparator = jest.fn((a: any, b: any) => a !== b);

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqual(arrA, arrB, badComparator)).toEqual(false);
});
});

it("should use deep-equal equivalents correctly on objects", () => {
const objA = { first: obj1, second: obj2 };
const objB = { second: { language: "elm" }, first: obj1 };
const objC = { second: { hi: "there", what: { depth: 3 } }, first: obj1 };
const objD = { second: { hi: "there", what: { depth: 3 } }, first: obj1 };

expect(eq(objA, objB)).toEqual(false);
expect(dequal(objA, objB)).toEqual(true);

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqual(objA, objB, dequal)).toEqual(true);
expect(shallowEqualObjects(objA, objB, dequal)).toEqual(true);
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqualObjects(objA, objC, dequal)).toEqual(false);
expect(shallowEqualObjects(objC, objD)).toEqual(false);
expect(shallowEqualObjects(objC, objD, dequal)).toEqual(true);
});

it("should use deep-equal equivalents correctly on arrays", () => {
const arrA = [{ first: obj1, second: obj2 }];
const arrB = [{ second: { language: "elm" }, first: obj1 }];
const arrC = [
arrA,
{ second: { hi: "there", what: { depth: 3 } }, first: obj1 },
];
const arrD = [
arrB,
{ second: { hi: "there", what: { depth: 3 } }, first: obj1 },
];

expect(eq(arrA, arrB)).toEqual(false);
expect(dequal(arrA, arrB)).toEqual(true);

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqual(arrA, arrB, dequal)).toEqual(true);
expect(shallowEqualArrays(arrA, arrB, dequal)).toEqual(true);
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this'll go away when #33 lands
expect(shallowEqualArrays(arrA, arrC, dequal)).toEqual(false);
expect(shallowEqualArrays(arrC, arrD)).toEqual(false);
expect(shallowEqualArrays(arrC, arrD, dequal)).toEqual(true);
});
});
7 changes: 5 additions & 2 deletions src/arrays.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Comparator, eq } from "./index";

export type validArrayValue<T> = T[] | null | undefined;

export default function shallowEqualArrays<T>(
arrA: validArrayValue<T>,
arrB: validArrayValue<T>
arrB: validArrayValue<T>,
comparator: Comparator = eq
): boolean {
if (arrA === arrB) {
return true;
Expand All @@ -19,7 +22,7 @@ export default function shallowEqualArrays<T>(
}

for (let i = 0; i < len; i++) {
if (arrA[i] !== arrB[i]) {
if (!comparator(arrA[i], arrB[i])) {
return false;
}
}
Expand Down
20 changes: 17 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@ import shallowEqualObjects, { validObjectValue } from "./objects";

type Comparable<T> = Record<string, T> | T[] | null | undefined;

function shallowEqual<T extends Comparable<T>>(a: T, b: T): boolean {
export type Comparator = (a: any, b: any) => boolean;
export function eq<T>(a: T, b: T): boolean {
return a === b;
}

function shallowEqual<T extends Comparable<T>>(
a: T,
b: T,
comparator: Comparator = eq
): boolean {
if (Array.isArray(a) || Array.isArray(b)) {
return shallowEqualArrays(a as validArrayValue<T>, b as validArrayValue<T>);
return shallowEqualArrays(
a as validArrayValue<T>,
b as validArrayValue<T>,
comparator
);
}

return shallowEqualObjects(
a as validObjectValue<T>,
b as validObjectValue<T>
b as validObjectValue<T>,
comparator
);
}

Expand Down
11 changes: 6 additions & 5 deletions src/objects.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Comparator, eq } from "./index";

export type validObjectValue<T> = Record<string, T> | null | undefined;

export default function shallowEqualObjects<T>(
objA: validObjectValue<T>,
objB: validObjectValue<T>
objB: validObjectValue<T>,
comparator: Comparator = eq
): boolean {
if (objA === objB) {
return true;
Expand All @@ -20,11 +23,9 @@ export default function shallowEqualObjects<T>(
return false;
}

for (let i = 0; i < len; i++) {
const key = aKeys[i];

for (const key of aKeys) {
if (
objA[key] !== objB[key] ||
!comparator(objA[key], objB[key]) ||
!Object.prototype.hasOwnProperty.call(objB, key)
) {
return false;
Expand Down