Skip to content

Commit

Permalink
[Enhancement]: private change detection
Browse files Browse the repository at this point in the history
  • Loading branch information
snewcomer committed Feb 1, 2021
1 parent 717dd82 commit da248a7
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 39 deletions.
22 changes: 17 additions & 5 deletions src/-private/change.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { IChange } from '../types';
/* import { IChange } from '../types'; */

export default class Change implements IChange {
value: any;
const VALUE = Symbol('__value__');

constructor(value: any) {
this.value = value;
export default class Change {
[VALUE]: unknown;

constructor(value: unknown) {
this[VALUE] = value;
}
}

// TODO: not sure why this function type guard isn't working
export const isChange = (maybeChange: unknown): maybeChange is Change =>
VALUE in (maybeChange as any);

export function getChangeValue(maybeChange: Change | unknown): any {
if (isChange(maybeChange)) {
return maybeChange[VALUE];
}
}
10 changes: 5 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from './-private/change';
import Change, { getChangeValue, isChange } from './-private/change';
import { getKeyValues, getKeyErrorValues } from './utils/get-key-values';
import lookupValidator from './utils/validator-lookup';
import { notifierForEvent } from './-private/evented';
Expand Down Expand Up @@ -946,8 +946,8 @@ export class BufferedChangeset implements IChangeset {
}

if (this.isObject(result)) {
if (result instanceof Change) {
return result.value;
if (isChange(result)) {
return getChangeValue(result);
}

const baseContent = this.safeGet(content, baseKey);
Expand All @@ -962,8 +962,8 @@ export class BufferedChangeset implements IChangeset {
}

// this comes after the isObject check to ensure we don't lose remaining keys
if (baseChanges instanceof Change && remaining.length === 0) {
return baseChanges.value;
if (isChange(baseChanges) && remaining.length === 0) {
return getChangeValue(baseChanges.value);
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ export type ValidatorMap =
| null
| undefined;

export interface IChange {
value: any;
}
// https://github.com/microsoft/TypeScript/pull/26797
/* export interface IChange { */
/* [s: symbol]: any; */
/* } */
export interface Changes {
[s: string]: IChange;
[s: string]: any; //IChange;
}

export interface Content {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/get-deep.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from '../-private/change';
import { isChange } from '../-private/change';

/**
* Handles both single key or nested string keys ('person.name')
Expand Down Expand Up @@ -47,7 +47,7 @@ export function getSubObject<T extends Record<string, any>>(root: T, path: strin
return undefined;
}

if (obj[parts[i]] instanceof Change) {
if (isChange(obj[parts[i]])) {
obj = obj[parts[i]].value;
} else {
obj = obj[parts[i]];
Expand Down
4 changes: 2 additions & 2 deletions src/utils/has-changes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import isObject from './is-object';
import Change from '../-private/change';
import { isChange } from '../-private/change';

export function hasChanges(changes: Record<string, any>): boolean {
for (let key in changes) {
if (changes[key] instanceof Change) {
if (isChange(changes[key])) {
return true;
}

Expand Down
6 changes: 3 additions & 3 deletions src/utils/has-key.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from '../-private/change';
import { isChange } from '../-private/change';

export function hasKey(record: Record<string, any>, path: string, safeGet: Function): boolean {
const keys = path.split('.');
Expand All @@ -20,7 +20,7 @@ export function pathInChanges(
path: string,
safeGet: Function
): boolean {
if (record instanceof Change) {
if (isChange(record)) {
return false;
}

Expand All @@ -32,7 +32,7 @@ export function pathInChanges(
return false;
}

if (keys[keys.length - 1] !== key && safeGet(obj, key) instanceof Change) {
if (keys[keys.length - 1] !== key && isChange(safeGet(obj, key))) {
return true;
}

Expand Down
12 changes: 6 additions & 6 deletions src/utils/merge-deep.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from '../-private/change';
import Change, { getChangeValue, isChange } from '../-private/change';
import normalizeObject from './normalize-object';

interface Options {
Expand Down Expand Up @@ -63,8 +63,8 @@ function buildPathToValue(
): Record<string, any> {
Object.keys(source).forEach((key: string): void => {
let possible = source[key];
if (possible && possible.hasOwnProperty('value') && possible instanceof Change) {
kv[[...possibleKeys, key].join('.')] = possible.value;
if (possible && isChange(possible)) {
kv[[...possibleKeys, key].join('.')] = getChangeValue(possible);
return;
}

Expand Down Expand Up @@ -105,7 +105,7 @@ function mergeTargetAndSource(target: any, source: any, options: Options): any {
if (
propertyIsOnObject(target, key) &&
isMergeableObject(source[key]) &&
!(source[key] instanceof Change)
!isChange(source[key])
) {
options.safeSet(
target,
Expand All @@ -114,8 +114,8 @@ function mergeTargetAndSource(target: any, source: any, options: Options): any {
);
} else {
let next = source[key];
if (next && next instanceof Change) {
return options.safeSet(target, key, next.value);
if (next && isChange(next)) {
return options.safeSet(target, key, getChangeValue(next));
}

return options.safeSet(target, key, normalizeObject(next));
Expand Down
10 changes: 5 additions & 5 deletions src/utils/normalize-object.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from '../-private/change';
import { getChangeValue, isChange } from '../-private/change';
import isObject from './is-object';

/**
Expand Down Expand Up @@ -30,17 +30,17 @@ export default function normalizeObject<T extends { [key: string]: any }>(
return target;
}

if (target instanceof Change) {
return target.value;
if (isChange(target)) {
return getChangeValue(target.value);
}

let obj = { ...target };

for (let key in obj) {
const next: any = obj[key];
if (next && isObj(next)) {
if (Object.prototype.hasOwnProperty.call(next, 'value') && next instanceof Change) {
obj[key] = next.value;
if (isChange(next)) {
obj[key] = getChangeValue(next);
} else {
try {
JSON.stringify(next);
Expand Down
6 changes: 3 additions & 3 deletions src/utils/object-tree-node.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ProxyHandler, Content } from '../types';
import isObject from './is-object';
import setDeep from './set-deep';
import Change from '../-private/change';
import Change, { getChangeValue, isChange } from '../-private/change';
import normalizeObject from './normalize-object';

const objectProxyHandler = {
Expand All @@ -23,8 +23,8 @@ const objectProxyHandler = {
}
}

if (childValue instanceof Change) {
return childValue.value;
if (isChange(childValue)) {
return getChangeValue(childValue);
}

if (isObject(childValue)) {
Expand Down
8 changes: 4 additions & 4 deletions src/utils/set-deep.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Change from '../-private/change';
import Change, { getChangeValue, isChange } from '../-private/change';
import isObject from './is-object';

interface Options {
Expand Down Expand Up @@ -62,11 +62,11 @@ export default function setDeep(
const isObj = isObject(target[prop]);
if (!isObj) {
options.safeSet(target, prop, {});
} else if (isObj && target[prop] instanceof Change) {
if (isObject(target[prop].value)) {
} else if (isObj && isChange(target[prop])) {
if (isObject(getChangeValue(target[prop]))) {
// if an object, we don't want to lose sibling keys
const siblings = findSiblings(target[prop].value, keys);
const resolvedValue = value instanceof Change ? value.value : value;
const resolvedValue = isChange(value) ? getChangeValue(value) : value;
target[prop] = new Change(
setDeep(siblings, keys.slice(1, keys.length).join('.'), resolvedValue, options)
);
Expand Down

0 comments on commit da248a7

Please sign in to comment.