UNPKG

@rimbu/deep

Version:

Tools to use handle plain JS objects as immutable objects

90 lines (89 loc) 4.07 kB
import { type IsAnyFunc, type IsArray, type IsPlainObj } from '@rimbu/base'; import type { Protected } from './internal.cjs'; import type { Tuple } from './tuple.cjs'; /** * 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;