From a4a8d67148f39acbf52b5728aba75573069fbd21 Mon Sep 17 00:00:00 2001 From: Tony Quetano Date: Fri, 30 Sep 2022 21:31:43 -0400 Subject: [PATCH] types and documentation cleanup --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ index.d.ts | 4 +-- src/copier.ts | 4 +-- src/index.ts | 2 +- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 64bc7c2..11a94c2 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ A [blazing fast](#benchmarks) deep object copier - [`copy`](#copy) - [`copyStrict`](#copystrict) - [`createCopier`](#createcopier) + - [Copier state](#copier-state) + - [`cache`](#cache) + - [`copier`](#copier) + - [`Constructor` / `prototype`](#constructor--prototype) - [`createStrictCopier`](#createstrictcopier) - [Copier methods](#copier-methods) - [Types supported](#types-supported) @@ -98,6 +102,93 @@ const copyShallow = createCopier({ }); ``` +Each internal copier method has the following contract: + +```ts +type InternalCopier = (value: Value, state: State) => Value; + +interface State { + Constructor: any; + cache: WeakMap; + copier: InternalCopier; + prototype: any; +} +``` + +Any method overriding the defaults must maintain this contract. + +#### Copier state + +##### `cache` + +If you want to maintain circular reference handling, then you'll need the methods to handle cache population for future lookups: + +```ts +function shallowlyCloneArray( + value: Value, + state: State +): Value { + const clone = [...value]; + + state.cache.set(value, clone); + + return clone; +} +``` + +##### `copier` + +`copier` is provided for recursive calls with deeply-nested objects. + +```ts +function deeplyCloneArray( + value: Value, + state: State +): Value { + const clone = []; + + state.cache.set(value, clone); + + value.forEach((item) => state.copier(item, state)); + + return clone; +} +``` + +Note above I am using `forEach` instead of a simple `map`. This is because it is highly recommended to store the clone in [`cache`](#cache) eagerly when deeply copying, so that nested circular references are handled correctly. + +##### `Constructor` / `prototype` + +Both `Constructor` and `prototype` properties are only populated with complex objects that are not standard objects or arrays. This is mainly useful for custom subclasses of these globals, or maintaining custom prototypes of objects. + +```ts +function deeplyCloneSubclassArray( + value: Value, + state: State +): Value { + const clone = new state.Constructor(); + + state.cache.set(value, clone); + + value.forEach((item) => clone.push(item)); + + return clone; +} + +function deeplyCloneCustomObject( + value: Value, + state: State +): Value { + const clone = Object.create(state.prototype); + + state.cache.set(value, clone); + + Object.entries(value).forEach(([k, v]) => (clone[k] = v)); + + return clone; +} +``` + ### `createStrictCopier` Create a custom copier based on the type-specific methods passed, but defaulting to the same functions normally used for `copyStrict`. This is useful if you want to squeeze out better performance while maintaining strict requirements, or perform something other than a strict deep copy. diff --git a/index.d.ts b/index.d.ts index 4011152..b06f3f4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,12 +18,12 @@ export interface CreateCopierOptions { set?: InternalCopier>; } -type InternalCopier = (value: Value, state: State) => Value; +type InternalCopier = (value: Value, state: State) => Value; export interface State { Constructor: any; cache: Cache; - copier: InternalCopier; + copier: InternalCopier; prototype: any; } diff --git a/src/copier.ts b/src/copier.ts index 8f73fe9..e930822 100644 --- a/src/copier.ts +++ b/src/copier.ts @@ -2,12 +2,12 @@ import { getCleanClone, getRegExpFlags } from './utils'; import type { Cache } from './utils'; -export type InternalCopier = (value: Value, state: State) => Value; +export type InternalCopier = (value: Value, state: State) => Value; export interface State { Constructor: any; cache: Cache; - copier: InternalCopier; + copier: InternalCopier; prototype: any; } diff --git a/src/index.ts b/src/index.ts index d6fea30..5132a24 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,7 +61,7 @@ const DEFAULT_STRICT_OPTIONS: Required = assign( function getTagSpecificCopiers( options: Required -): Record { +): Record> { return { Array: options.array, ArrayBuffer: options.arrayBuffer,