type Value = Primitive | ValueArray | ValueObject;
type Primitive = string | number | boolean | symbol | bigint | null | undefined;
interface ValueArray extends ReadonlyArray<Value> {
}
declare const VALUE_OBJECT_BRAND: unique symbol;
declare abstract class ValueObject<T extends object = object> {
    protected readonly props: Readonly<T>;
    readonly [VALUE_OBJECT_BRAND] = true;
    protected constructor(props: Readonly<T>, values: ValueArray);
}
type ReadonlyValue<T extends Value> = T extends readonly [infer U extends Value, ...(infer R extends readonly Value[])] ? R extends [...never[]] ? readonly [ReadonlyValue<U>] : readonly [ReadonlyValue<U>, ...ReadonlyValue<R>] : T extends readonly (infer U extends Value)[] ? readonly ReadonlyValue<U>[] : T;
declare function isPrimitive(x: unknown): x is Primitive;
declare function isValueArray(x: unknown): x is ValueArray;
declare function isValueObject(x: unknown): x is ValueObject;
declare function isValue(x: unknown): x is Value;
/** A cache tree node used to store an object {@link WeakRef} and a {@link Map} of child nodes, both optional. */
interface CacheTreeNode {
    children: Map<unknown, CacheTreeNode> | null;
    instanceRef: WeakRef<object> | null;
}
/**
 * A value object cache that can be used to make value objects behave like primitive types, i.e. if two variables `a`
 * and `b` point to an instance of the same class and have the same value, then `a === b`, otherwise `a !== b`.
 *
 * To achieve this, the cache can be queried with three arguments: a class constructor, an array of values, and a
 * factory function. Values represent the "identity" of an instance: all calls to the cache with the same constructor
 * and instance parameters (according to the [same-value-zero equality](
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality))
 * will return the same instance. If the cache already contains an instance of this class with the same values, then it
 * is returned. Otherwise, the provided factory is called to create a new instance, which is then stored in the cache
 * and returned - all the following calls to the cache with the same constructor and values will now return this
 * instance until it is garbage-collected. Because, as the cache only stores weak references to the instances and their
 * constructors, they can still be garbage-collected once they become unreachable.
 *
 * While value objects aren't usually expected to have the same identity when they're equal, making sure they do can
 * make life easier in situations where specifying a custom equality function isn't practical or even doable, such as
 * when using React hooks like {@link useCallback}, {@link useMemo}, {@link useEffect}, etc.
 *
 * // TODO update doc
 *
 * @see https://en.wikipedia.org/wiki/Value_object
 *
 * @example
 * ```ts
 * abstract class Dimension<Unit extends string> {
 *   constructor(
 *     readonly scalar: number,
 *     readonly unit: Unit,
 *   ) {
 *     Object.freeze(this);
 *     return valueObjectCache.getInstance(this.constructor, [scalar, unit], () => this);
 *   }
 * }
 *
 * type LengthUnit = 'mm' | 'm' | 'km';
 * class Length extends Dimension<LengthUnit> {}
 * class OtherLength extends Dimension<LengthUnit> {}
 *
 * console.log(new Length(1, 'm') === new Length(1, 'm')); // outputs 'true'
 * console.log(new Length(1, 'm') === new Length(2, 'm')); // outputs 'false'
 * console.log(new Length(1, 'm') === new OtherLength(1, 'm')); // outputs 'false'
 * ```
 */
declare const valueObjectCache: {
    readonly "__#1@#rootNode": CacheTreeNode;
    readonly "__#1@#finalizationRegistry": FinalizationRegistry<readonly unknown[]>;
    "__#1@#get"<T extends object>(constructor: Function, values: ValueArray, factory: () => T): T;
    /** Look for an instance of the provided class constructor matching the provided values. If a matching instance is
     * found then it is returned, otherwise the factory function is called to create a new instance, which is then stored
     * in the cache and returned - all future calls to this method made with the same constructor and values will return
     * this instance until it is garbage-collected. */
    getObject<const T extends object>(constructor: Function, values: ValueArray, factory: () => T): T;
    /** Look for an {@link Array} containing a specific list of values in the cache. If a matching {@link Array} is found
     * then it is returned, otherwise a new {@link Array} is stored in the cache and returned. All returned arrays are
     * frozen (readonly). */
    getArray<const T extends ValueArray>(values: T): ReadonlyValue<T>;
    getValue<const T extends Value>(value: T): ReadonlyValue<T>;
};

export { type Primitive, type ReadonlyValue, type Value, type ValueArray, ValueObject, isPrimitive, isValue, isValueArray, isValueObject, valueObjectCache };
