@rimbu/deep
Version:
Tools to use handle plain JS objects as immutable objects
90 lines (89 loc) • 4.07 kB
text/typescript
import { type IsAnyFunc, type IsArray, type IsPlainObj } from '@rimbu/base';
import type { Protected } from './internal.mjs';
import type { Tuple } from './tuple.mjs';
/**
* A type to determine the allowed input type for the `patch` function.
* @typeparam T - the input type to be patched
*/
export type Patch<T, C = T> = Patch.Entry<T, C, T, T>;
export declare namespace Patch {
/**
* The entry type for a (nested) patch. Can be either a patch object or a function accepting the nested patch function and returning a patch object.
* @typeparam T - the input value type
* @typeparam C - a utility type
* @typeparam P - the parent type
* @typeparam R - the root object type
*/
type Entry<T, C, P, R> = IsAnyFunc<T> extends true ? T : IsPlainObj<T> extends true ? Patch.WithResult<T, P, R, Patch.Obj<T, C, R>> : Tuple.IsTuple<T> extends true ? Patch.WithResult<T, P, R, T | Patch.Tup<T, C, R>> : IsArray<T> extends true ? Patch.WithResult<T, P, R, T> : Patch.WithResult<T, P, R, T>;
/**
* Either result type S, or a patch function with the value type, the parent type, and the root type.
* @typeparam T - the value type
* @typeparam P - the parent type
* @typeparam R - the root type
* @typeparam S - the result type
*/
type WithResult<T, P, R, S> = S | Patch.Func<T, P, R, S>;
/**
* A function patch type that is a function taking the current value, the parent and root values,
* and returns a return value.
* @typeparam T - the value type
* @typeparam P - the parent type
* @typeparam R - the root type
* @typeparam S - the result type
*/
type Func<T, P, R, S> = (current: Protected<T>, parent: Protected<P>, root: Protected<R>) => Protected<S>;
/**
* A type defining the allowed patch values for tuples.
* @typeparam T - the input tuple type
* @typeparam C - a utility type
* @typeparam R - the root type
*/
type Tup<T, C, R> = {
[K in Tuple.KeysOf<T>]?: Patch.Entry<T[K & keyof T], C[K & keyof C], T, R>;
} & NotIterable;
/**
* Utility type to exclude Iterable types.
*/
type NotIterable = {
[Symbol.iterator]?: never;
};
/**
* A type defining the allowed patch values for objects.
* @typeparam T - the input value type
* @typeparam C - a utility type
* @typeparam R - the root object type
*/
type Obj<T, C, R> = T | Patch.ObjProps<T, C, R>[];
/**
* A type defining the allowed patch values for object properties.
* @typeparam T - the input value type
* @typeparam C - a utility type
* @typeparam R - the root object type
*/
type ObjProps<T, C, R> = {
[K in keyof C]?: K extends keyof T ? Patch.Entry<T[K], C[K], T, R> : never;
};
}
/**
* Returns an immutably updated version of the given `value` where the given `patchItems` have been
* applied to the result.
* The Rimbu patch notation is as follows:
* - if the target is a simple value or array, the patch can be the same type or a function returning the same type
* - if the target is a tuple (array of fixed length), the patch be the same type or an object containing numeric keys with patches indicating the tuple index to patch
* - if the target is an object, the patch can be the same type, or an array containing partial keys with their patches for the object
* @typeparam T - the type of the value to patch
* @typeparam TE - a utility type
* @typeparam TT - a utility type
* @param value - the input value to patch
* @param patchItem - the `Patch` value to apply to the input value
* @example
* ```ts
* const input = { a: 1, b: { c: true, d: 'a' } }
* patch(input, [{ a: 2 }]) // => { a: 2, b: { c: true, d: 'a' } }
* patch(input, [{ b: [{ c: (v) => !v }] }] )
* // => { a: 1, b: { c: false, d: 'a' } }
* patch(input: [{ a: (v) => v + 1, b: [{ d: 'q' }] }] )
* // => { a: 2, b: { c: true, d: 'q' } }
* ```
*/
export declare function patch<T, TE extends T = T, TT = T>(value: T, patchItem: Patch<TE, T & TT>): T;