@rimbu/deep
Version:
Tools to use handle plain JS objects as immutable objects
141 lines (140 loc) • 6.14 kB
text/typescript
import { type IsAnyFunc, type IsArray, type IsPlainObj, type NotIterable } from '@rimbu/base';
import type { Protected } from './internal.mjs';
import type { Tuple } from './tuple.mjs';
/**
* The type to determine the allowed input values for the `match` function.
* @typeparam T - the type of value to match
* @typeparam C - utility type
*/
export type Match<T, C extends Partial<T> = Partial<T>> = Match.Entry<T, C, T, T>;
export declare namespace Match {
/**
* Determines the various allowed match types for given type `T`.
* @typeparam T - the input value type
* @typeparam C - 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 ? Match.WithResult<T, P, R, Match.Obj<T, C, P, R>> : IsArray<T> extends true ? Match.Arr<T, C, P, R> | Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[] | Match.Func<T, P, R, Match.Arr<T, C, P, R> | Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[]> : Match.WithResult<T, P, R, {
[K in keyof C]: C[K & keyof T];
}>;
/**
* The type that determines allowed matchers for objects.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam P - the parent type
* @typeparam R - the root object type
*/
type Obj<T, C, P, R> = Match.ObjProps<T, C, R> | Match.CompoundForObj<T, C, P, R>;
/**
* The type to determine allowed matchers for object properties.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam R - the root object type
*/
type ObjProps<T, C, R> = {
[K in keyof C]?: K extends keyof T ? Match.Entry<T[K], C[K], T, R> : never;
};
/**
* The type that determines allowed matchers for arrays/tuples.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam P - the parent type
* @typeparam R - the root object type
*/
type Arr<T, C, P, R> = C | Match.CompoundForArr<T, C, P, R> | Match.TraversalForArr<T, C, R> | (Match.TupIndices<T, C, R> & {
[K in Match.CompoundType | Match.ArrayTraversalType]?: never;
});
/**
* A type that either directly results in result type `S` or is a function taking the value, parent, and root values, and
* returns a value of type `S`.
* @typeparam T - the input value type
* @typeparam P - the parent type
* @typeparam R - the root object type
* @typeparam S - the result type
*/
type WithResult<T, P, R, S> = S | Match.Func<T, P, R, S>;
/**
* Type used to determine the allowed function types. Always includes booleans.
* @typeparam T - the input value type
* @typeparam P - the parent type
* @typeparam R - the root object type
* @typeparam S - the allowed return value type
*/
type Func<T, P, R, S> = (current: Protected<T>, parent: Protected<P>, root: Protected<R>) => boolean | S;
/**
* Type used to indicate an object containing matches for tuple indices.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam R - the root object type
*/
type TupIndices<T, C, R> = {
[K in Tuple.KeysOf<C>]?: Match.Entry<T[K & keyof T], C[K], T, R>;
} & NotIterable;
/**
* Compound keys used to indicate the type of compound.
*/
type CompoundType = 'every' | 'some' | 'none' | 'single';
/**
* Keys used to indicate an array match traversal.
*/
type ArrayTraversalType = `${CompoundType}Item`;
/**
* Compount matcher for objects, can only be an array staring with a compound type keyword.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam P - the parent type
* @typeparam R - the root object type
*/
type CompoundForObj<T, C, P, R> = [
Match.CompoundType,
...Match.Entry<T, C, P, R>[]
];
/**
* Defines an object containing exactly one `CompoundType` key, having an array of matchers.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam P - the parent type
* @typeparam R - the root object type
*/
type CompoundForArr<T, C, P, R> = {
[K in Match.CompoundType]: {
[K2 in Match.CompoundType]?: K2 extends K ? Match.Entry<T, C, P, R>[] : never;
};
}[Match.CompoundType];
/**
* Defines an object containing exactly one `TraversalType` key, having a matcher for the array element type.
* @typeparam T - the input value type
* @typeparam C - utility type
* @typeparam R - the root object type
*/
type TraversalForArr<T, C, R> = {
[K in Match.ArrayTraversalType]: {
[K2 in Match.ArrayTraversalType]?: K2 extends K ? Match.Entry<T[number & keyof T], C[number & keyof C], T, R> : never;
};
}[Match.ArrayTraversalType];
/**
* Utility type for collecting match failure reasons
*/
type FailureLog = string[];
}
/**
* Returns true if the given `value` object matches the given `matcher`, false otherwise.
* @typeparam T - the input value type
* @typeparam C - utility type
* @param source - the value to match (should be a plain object)
* @param matcher - a matcher object or a function taking the matcher API and returning a match object
* @param failureLog - (optional) a string array that can be passed to collect reasons why the match failed
* @example
* ```ts
* const input = { a: 1, b: { c: true, d: 'a' } }
* match(input, { a: 1 }) // => true
* match(input, { a: 2 }) // => false
* match(input, { a: (v) => v > 10 }) // => false
* match(input, { b: { c: true }}) // => true
* match(input, (['every', { a: (v) => v > 0 }, { b: { c: true } }]) // => true
* match(input, { b: { c: (v, parent, root) => v && parent.d.length > 0 && root.a > 0 } })
* // => true
* ```
*/
export declare function match<T, C extends Partial<T> = Partial<T>>(source: T, matcher: Match<T, C>, failureLog?: Match.FailureLog): boolean;