Skip to content

Commit

Permalink
renames, better typing, and update README to reflect `createCustomEqu…
Browse files Browse the repository at this point in the history
…al` options change
  • Loading branch information
planttheidea committed Feb 27, 2023
1 parent 30d8418 commit 3857342
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 180 deletions.
26 changes: 7 additions & 19 deletions DEV_ONLY/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
strictShallowEqual,
} from '../src';

import type { State } from '../src/internalTypes';

document.body.style.backgroundColor = '#1d1d1d';
document.body.style.color = '#d5d5d5';
document.body.style.margin = '0px';
Expand Down Expand Up @@ -232,23 +230,15 @@ const object4 = {
};

const doesNotEverEqualOne = createCustomEqual<undefined>({
createState: (comparator) => ({
equals(
a: any,
b: any,
_keyA,
_keyB,
_parentA,
_parentB,
state: State<undefined>,
) {
createInternalComparator:
(comparator) =>
(a: any, b: any, _keyA, _keyB, _parentA, _parentB, state) => {
if (typeof a === 'number' || typeof b === 'number') {
return a !== 1 && b !== 1;
}

return Object.keys(a).every((key) => comparator(a[key], b[key], state));
},
}),
});

console.log('true', doesNotEverEqualOne(object1, object2));
Expand Down Expand Up @@ -354,24 +344,22 @@ console.groupEnd();
console.group('targeted custom');

const isDeepEqualOrFooMatchesMeta = createCustomEqual<'bar'>({
createState: (compare) => ({
equals(a, b, _keyA, _keyB, _parentA, _parentB, state) {
createInternalComparator:
(compare) => (a, b, _keyA, _keyB, _parentA, _parentB, state) => {
return a === state.meta || b === state.meta || compare(a, b, state);
},
meta: 'bar',
}),
createState: () => ({ meta: 'bar' }),
});

console.log(
'shallow',
isDeepEqualOrFooMatchesMeta({ foo: 'bar' }, { foo: 'baz' }, 'bar'),
isDeepEqualOrFooMatchesMeta({ foo: 'bar' }, { foo: 'baz' }),
);
console.log(
'deep',
isDeepEqualOrFooMatchesMeta(
{ nested: { foo: 'bar' } },
{ nested: { foo: 'baz' } },
'bar',
),
);

Expand Down
50 changes: 22 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,24 +269,13 @@ Creates a custom equality comparator that will be used on nested values in the o
The signature is as follows:

```ts
interface BaseCircular extends Pick<WeakMap<any, any>, 'delete' | 'get'> {
set(key: object, value: any): any;
interface Cache<Key extends object, Value> {
delete(key: Key): boolean;
get(key: Key): Value | undefined;
set(key: Key, value: any): any;
}

interface DefaultState<Meta> {
readonly cache: undefined | BaseCircular;
readonly equals: InternalEqualityComparator<Meta>;
meta: Meta;
readonly strict: boolean;
}

type EqualityComparator<Meta> = <A, B>(
a: A,
b: B,
state: State<Meta>,
) => boolean;

interface CreateComparatorCreatorOptions<Meta> {
interface ComparatorConfig<Meta> {
areArraysEqual: TypeEqualityComparator<any[], Meta>;
areDatesEqual: TypeEqualityComparator<Date, Meta>;
areMapsEqual: TypeEqualityComparator<Map<any, any>, Meta>;
Expand All @@ -300,20 +289,25 @@ interface CreateComparatorCreatorOptions<Meta> {
areTypedArraysEqual: TypeEqualityComparatory<TypedArray, Meta>;
}

interface CustomEqualCreatorOptions<Meta> {
function createCustomEqual<Meta>(options: {
circular?: boolean;
comparator?: EqualityComparator<Meta>;
createCustomConfig?: CreateCustomComparatorConfig<Meta>;
createInternalComparator?: <Meta>(
compare: EqualityComparator<Meta>,
) => InternalEqualityComparator<Meta>;
createState?: CreateState<Meta>;
createCustomConfig?: (
defaultConfig: ComparatorConfig<Meta>,
) => Partial<ComparatorConfig<Meta>>;
createInternalComparator?: (
compare: <A, B>(a: A, b: B, state: State<Meta>) => boolean,
) => (
a: any,
b: any,
indexOrKeyA: any,
indexOrKeyB: any,
parentA: any,
parentB: any,
state: State<Meta>,
) => boolean;
createState?: () => { cache?: Cache; meta?: Meta };
strict?: boolean;
}

function createCustomEqual(
options: CustomEqualCreatorOptions<Meta>,
): <A, B>(a: A, b: B, metaOverride?: Meta) => boolean;
}): <A, B>(a: A, b: B, metaOverride?: Meta) => boolean;
```

Create a custom equality comparator. This allows complete control over building a bespoke equality method, in case your use-case requires a higher degree of performance, legacy environment support, or any other non-standard usage. The [recipes](#recipes) provide examples of use in different use-cases, but if you have a specific goal in mind and would like assistance feel free to [file an issue](https://github.com/planttheidea/fast-equals/issues).
Expand Down
17 changes: 10 additions & 7 deletions __tests__/comparator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { createComparator } from '../src/comparator';
import {
createEqualityComparator,
createInternalEqualityComparator,
} from '../src/comparator';
import {
areArraysEqual,
areDatesEqual,
Expand All @@ -9,7 +12,7 @@ import {
areSetsEqual,
areTypedArraysEqual,
} from '../src/equals';
import { createInternalComparator, createIsCircular } from '../src/utils';
import { createIsCircular } from '../src/utils';

import type { InternalEqualityComparator, State } from '../src/internalTypes';

Expand All @@ -31,7 +34,7 @@ const CIRCULAR_COMPARATOR_OPTIONS = {
areSetsEqual: createIsCircular(areSetsEqual),
};

describe('createComparator', () => {
describe('createEqualityComparator', () => {
[
{
createState: <Meta>(
Expand Down Expand Up @@ -62,8 +65,8 @@ describe('createComparator', () => {
].forEach(({ createState, name, options }) => {
describe(name, () => {
it('should default to a deep-equal setup when no equality comparator is provided', () => {
const comparator = createComparator(options);
const meta = createState(createInternalComparator(comparator));
const comparator = createEqualityComparator(options);
const meta = createState(createInternalEqualityComparator(comparator));

const a = { foo: { bar: 'baz' } };
const b = { foo: { bar: 'baz' } };
Expand All @@ -81,7 +84,7 @@ describe('createComparator', () => {
['foo', 'bar'],
]);

const comparator = createComparator(options);
const comparator = createEqualityComparator(options);
const state = createState(
(
a: any,
Expand Down Expand Up @@ -113,7 +116,7 @@ describe('createComparator', () => {
describe('custom', () => {
it('should call the custom comparator with the correct params', () => {
const customComparatorMock = jest.fn();
const comparator = createComparator(STANDARD_COMPARATOR_OPTIONS);
const comparator = createEqualityComparator(STANDARD_COMPARATOR_OPTIONS);
const state: State<'META'> = {
cache: undefined,
equals(...args) {
Expand Down
3 changes: 1 addition & 2 deletions __tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
strictDeepEqual,
strictShallowEqual,
} from '../src';
import { BaseCircular } from '../src/internalTypes';

describe('exports', () => {
[
Expand Down Expand Up @@ -168,7 +167,7 @@ describe('circular', () => {
});

describe('createCustomCircularEqual', () => {
function getFakeWeakMap(): BaseCircular {
function getFakeWeakMap() {
const entries: [object, object][] = [];

return {
Expand Down
34 changes: 17 additions & 17 deletions __tests__/recipes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { createCustomEqual, sameValueZeroEqual } from '../src/index';

import type {
BaseCircular,
CreateState,
TypeEqualityComparator,
} from '../src/internalTypes';
import type { CreateState, TypeEqualityComparator } from '../src/internalTypes';

describe('recipes', () => {
describe('createCustomEqual', () => {
Expand Down Expand Up @@ -81,15 +77,19 @@ describe('recipes', () => {
value: string;
}

const createState: CreateState<Meta> = (deepEqual) => ({
equals: (a, b, _keyA, _keyB, _parentA, _parentB, state) =>
deepEqual(a, b, state) ||
a === state.meta.value ||
b === state.meta.value,
meta: { value: 'baz' },
});
const createState: CreateState<Meta> = () => ({ meta: { value: 'baz' } });

const deepEqual = createCustomEqual({ createState });
const deepEqual = createCustomEqual<Meta>({
createInternalComparator:
(deepEqual) => (a, b, _keyA, _keyB, _parentA, _parentB, state) => {
return (
deepEqual(a, b, state) ||
a === state.meta.value ||
b === state.meta.value
);
},
createState,
});

it('should verify the object itself', () => {
const a = { bar: 'bar' };
Expand Down Expand Up @@ -258,11 +258,11 @@ describe('recipes', () => {
customValue: string;
}

function getCache(): BaseCircular {
function getCache() {
const entries: Array<[object, any]> = [];

return {
delete(key) {
delete(key: object) {
for (let index = 0; index < entries.length; ++index) {
if (entries[index]![0] === key) {
entries.splice(index, 1);
Expand All @@ -273,15 +273,15 @@ describe('recipes', () => {
return false;
},

get(key) {
get(key: object) {
for (let index = 0; index < entries.length; ++index) {
if (entries[index]![0] === key) {
return entries[index]![1];
}
}
},

set(key, value) {
set(key: object, value: any) {
for (let index = 0; index < entries.length; ++index) {
if (entries[index]![0] === key) {
entries[index]![1] = value;
Expand Down
4 changes: 2 additions & 2 deletions recipes/legacy-circular-equal-support.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Legacy environment support for circular equal comparators

Starting in `4.x.x`, `WeakMap` is expected to be available in the environment. All modern browsers support this global object, however there may be situations where a legacy environmental support is required (example: IE11). If you need to support such an environment, creating a custom comparator that uses a custom cache implementation with the same contract is a simple solution.
Starting in `4.x.x`, `WeakMap` is expected to be available in the environment. All modern browsers support this global object, however there may be situations where a legacy environmental support is required (example: IE11). If you need to support such an environment and polyfilling is not an option, creating a custom comparator that uses a custom cache implementation with the same contract is a simple solution.

```ts
import { createCustomEqual, sameValueZeroEqual } from 'fast-equals';

function getCache(): BaseCircular {
function getCache(): Cache {
const entries: Array<[object, any]> = [];

return {
Expand Down
2 changes: 1 addition & 1 deletion recipes/legacy-regexp-support.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Legacy environment support for `RegExp` comparators

Starting in `4.x.x`, `RegExp.prototype.flags` is expected to be available in the environment. All modern browsers support this feature, however there may be situations where a legacy environmental support is required (example: IE11). If you need to support such an environment, creating a custom comparator that uses a more verbose comparison of all possible flags is a simple solution.
Starting in `4.x.x`, `RegExp.prototype.flags` is expected to be available in the environment. All modern browsers support this feature, however there may be situations where a legacy environmental support is required (example: IE11). If you need to support such an environment and polyfilling is not an option, creating a custom comparator that uses a more verbose comparison of all possible flags is a simple solution.

```ts
import { createCustomEqual, sameValueZeroEqual } from 'deep-Equals';
Expand Down
Loading

0 comments on commit 3857342

Please sign in to comment.