import { Signal, WritableSignal, DestroyRef } from '@angular/core'; import { AbstractControl, FormControlState, FormControlStatus, ValidationErrors, FormArray, FormGroup, FormControl } from '@angular/forms'; /** * Represents a generic object with string keys and values of any type. * Useful as a base type for utility functions and signal helpers. * * @typedef {Record} ObjectType */ type ObjectType = Record; /** * Represents an object where each property is a read-only Signal. * Useful for mapping state objects to their signal equivalents. */ type SignalObj = { [Key in keyof T]: Signal; }; /** * Represents an object where each property is a WritableSignal. * Allows for direct modification of the signal values within the object structure. */ type SignalObjWritable = { [Key in keyof T]: WritableSignal; }; /** * A type representing either a Signal of type T or the raw value of type T. * Useful for inputs that can accept both static values and reactive signals. */ type SignalOrValue = Signal | T; /** * Represents an object where each property can be either a Signal of a certain type * or the raw value of that type. * * @template TValues The type of the values contained within the SignalOrValue properties. * Defaults to a union of ObjectType, string, number, or undefined. */ type SignalOrValueObj = TValues extends object ? { [Key in keyof TValues]: SignalOrValue; } : Record>; /** * Converts a plain object into a writable signal object. * Each property of the source object becomes a WritableSignal initialized with the property's value. * * @param src - The source object to convert. * @returns An object with the same keys as the source, but with values wrapped in WritableSignals. */ declare function toSignalObj(src: T): SignalObjWritable; /** * Converts a signal object into a plain object. * Each property of the signal object becomes a value from the signal. * * @param src - The signal object to convert. * @returns An object with the same keys as the source, but with values from the signals. */ declare function fromSignalObj(src: SignalOrValueObj): TData; /** * Unwraps a value that might be a Signal. * If the input is a Signal, it returns the signal's value. * If the input is a raw value, it returns the value itself. * * @param value - The signal or value to unwrap. * @returns The raw value of type T. */ declare function signalOrValue(value: SignalOrValue): T; declare function signalOrFunction(value: SignalOrValue | ((...args: FnArgs) => T), ...fnArgs: FnArgs): T; /** * A function interface for a signal-based notification mechanism. * Calling the function returns the current notification count. * Calling `notify()` increments the count, triggering any listeners. */ interface SignalNotifier { (): number; /** * Triggers a notification by incrementing the internal signal value. */ notify: () => void; } /** * Creates a signal-based notifier function. * * Each call to the returned function returns the current notification count. * Calling `notify()` increments the count, triggering any listeners. * * @returns {SignalNotifier} A notifier function with a `notify` method. * * @example * const notifier = signalNotifier(); * effect(() => { * notifier(); // Reacts to notifications * }); * notifier.notify(); // Triggers the effect */ declare function signalNotifier(): SignalNotifier; /** * A writable signal enhanced with debounce capabilities. * * Extends `WritableSignal` so it can be read and written like a normal signal, * but also exposes a `setDebounced` method that applies values after a configurable delay * and an `isLoading` signal that indicates whether a debounced update is pending. * * @typeParam T - The type of the signal's value. */ interface SignalDebounce extends WritableSignal { /** Sets the signal value after the configured debounce delay. */ setDebounced(value: T): void; /** Reactive flag that is `true` while a debounced update is pending. */ isLoading: Signal; } /** * Creates a debounced writable signal. * * The returned signal can be written to instantly via its `WritableSignal` interface * **or** through `setDebounced(value)` which delays the commit by `debounceTime` ms. * While a debounced write is pending, `isLoading()` returns `true`. * * If a reactive `params` function is supplied, the signal will also track that * source and debounce upstream changes (requires an injection context). * * @typeParam T - The type of the signal's value. * @param options - Configuration object. * @param options.source - Optional reactive source function whose return value * is tracked and debounced into the signal. * @param options.debounceTime - Delay in milliseconds before a debounced value * is committed. * @param options.initialValue - Optional initial value for the signal. * @param options.injector - Optional Angular `Injector` to use for setting up * reactive tracking. Required if `source` is provided and this function is called * outside of an injection context. * @returns A `SignalDebounce` instance. * * @example * ```ts * // Simple debounced signal with an initial value * const search = signalDebounce({ debounceTime: 300, initialValue: '' }); * search.setDebounced('hello'); // commits after 300 ms * * // Tracking a reactive source * const query = signal('angular'); * const debounced = signalDebounce({ source: () => query(), debounceTime: 500 }); * ``` */ declare function signalDebounce(options: { source?: () => T; debounceTime: number; initialValue?: T; injector?: unknown; }): SignalDebounce; /** * A reactive wrapper around a `Set` backed by Angular signals. * All mutations produce a new `Set` instance, ensuring signal-based change detection works correctly. * * @template T The type of elements stored in the set. Defaults to `number`. */ interface SignalSet { /** The current underlying `Set` (read via signal). */ (): Set; /** The number of elements in the set (reactive). */ size: Signal; /** A computed signal that returns the set contents as an array. */ toArray: Signal; /** Adds the element if absent, removes it if present. */ toggle: (id: T) => void; /** Returns `true` if the element exists in the set. */ has: (id: T) => boolean; /** Adds an element to the set. */ add: (id: T) => void; /** Removes an element from the set. */ delete: (id: T) => void; /** Removes all elements from the set. */ clear: () => void; /** Returns a human-readable string representation, e.g. `SignalSet(1, 2, 3)`. */ toString: () => string; /** Converts the set to a JSON-compatible array. e.g. `[1, 2, "a"]` */ toJSON: () => T[]; } /** * Creates a reactive `SignalSet` backed by Angular signals. * * Every mutation creates a new `Set`, so Angular's signal equality check triggers updates. * * @template T The element type. Defaults to `number`. * @param initialValue An optional iterable to seed the set with. * @returns A {@link SignalSet} instance. * * @example * ```ts * const selected = signalSet(); * selected.add(1); * selected.toggle(2); * console.log(selected.toArray()); // [1, 2] * selected.toggle(1); * console.log(selected.has(1)); // false * ``` */ declare function signalSet(initialValue?: Iterable | (() => Iterable)): SignalSet; /** * Resolves `T` to itself when it is a valid object key (`string | number | symbol`), * otherwise falls back to `string`. Used to type-safe `Record` conversions. */ type ToKey = T extends string | number | symbol ? T : string; /** * A reactive wrapper around a `Map` backed by Angular signals. * All mutations produce a new `Map` instance, ensuring signal-based change detection works correctly. * * @template K The key type. Defaults to `string`. * @template V The value type. Defaults to `unknown`. */ interface SignalMap { /** The current underlying `Map` (read via signal). */ (): Map; /** The number of entries in the map (reactive). */ size: Signal; /** A computed signal that returns the map keys as an array. */ keys: Signal; /** A computed signal that returns the map values as an array. */ values: Signal; /** A computed signal that returns the map entries as an array of `[key, value]` tuples. */ entries: Signal<[K, V][]>; /** Returns the value associated with `key`, or `undefined` if absent. */ get: (key: K) => V | undefined; /** Returns `true` if the map contains the given `key`. */ has: (key: K) => boolean; /** Sets (or overwrites) the value for `key` and returns the updated `Map`. */ set: (key: K, value: V) => Map; /** Removes the entry for `key`. Returns `true` if the key existed. */ delete: (key: K) => boolean; /** Removes all entries from the map. */ clear: () => void; /** Returns a human-readable string, e.g. `SignalMap(a => 1, b => 2)`. */ toString: () => string; /** Converts the map to a JSON-compatible object. */ toJSON: () => Record, V>; } /** * Creates a reactive `SignalMap` backed by Angular signals. * * Every mutation creates a new `Map`, so Angular's signal equality check triggers updates. * Accepts either an iterable of `[key, value]` pairs or a plain object as the initial value. * * @template K The key type. Defaults to `string`. * @template V The value type. Defaults to `unknown`. * @param initialValue An optional iterable of entries or a plain object to seed the map. * @returns A {@link SignalMap} instance. * * @example * ```ts * const cache = signalMap({ a: 1, b: 2 }); * cache.set('c', 3); * console.log(cache.keys()); // ['a', 'b', 'c'] * cache.delete('a'); // true * console.log(cache.toJSON()); // {b:2,c:3} * ``` */ declare function signalMap(initialValue?: Iterable<[K, V]> | Record, V> | (() => Iterable<[K, V]> | Record, V>)): SignalMap; /** * Creates a reactive object backed by Angular signals. * * Every property is stored as a `WritableSignal`. Reading a property * (e.g. `obj.name` or `obj['name']`) calls the signal — so it's * automatically tracked in templates, `computed()`, and `effect()`. * Setting a property calls `signal.set()`, which triggers reactivity. * * @example * // In a component: * protected person = signalObject({ name: 'dvirus', age: 30 }); * * // In the template (reactive — updates automatically): * // {{ person.name }} * * // In the class: * // person.name = 'changed'; → triggers re-render * // person['age'] = 31; → triggers re-render */ type SignalObject = T & { /** * Call the SignalObject as a function to get a reactive snapshot. * * @example * const person = signalObject({ name: 'dvirus', age: 30 }); * person(); // { name: 'dvirus', age: 30 } — tracked by Angular */ (): T; /** * A computed signal that returns a plain snapshot of all properties. * Reading this tracks ALL properties — any property change triggers reactivity. * * @example * effect(() => console.log(person.$snapshot())); * // logs whenever ANY property changes * * const label = computed(() => { * const snap = person.$snapshot(); * return `${snap.name} (${snap.age})`; * }); */ /** * Spreads one or more objects (plain or reactive) into this SignalObject. * Mimics `Object.assign(this, ...sources)` / `{ ...this, ...a, ...b }`. * * - Existing keys → updates the signal (triggers reactivity) * - New keys → creates a new signal and bumps the version * - Accepts plain objects and SignalObjects interchangeably * * @param sources - One or more plain objects or SignalObjects to merge in * * @example * const person = signalObject({ name: 'dvirus' }); * person.$assign({ age: 30 }, otherSignalObj); */ $assign(...sources: Partial[]): void; }; /** * Creates a reactive object backed by Angular signals. * * Every property is stored as a `WritableSignal`. Reading a property * (e.g. `obj.name` or `obj['name']`) calls the signal — so it's * automatically tracked in templates, `computed()`, and `effect()`. * Setting a property calls `signal.set()`, which triggers reactivity. * * @param initialValue - The plain object to make reactive * @returns A `SignalObject` proxy with reactive property access, `$snapshot`, and `$assign` * * @example * // In a component: * protected person = signalObject({ name: 'dvirus', age: 30 }); * * // In the template (reactive — updates automatically): * // {{ person.name }} * * // In the class: * person.name = 'changed'; // triggers re-render * person['age'] = 31; // triggers re-render * * // Spread (plain snapshot): * const copy = { ...person }; // { name: 'changed', age: 31 } * * // Track all properties reactively: * effect(() => console.log(person.$snapshot())); */ declare function signalObject(initialValue: T): SignalObject; /** * Type guard — checks if a value is a reactive SignalObject proxy. * * @example * isSignalObject(signalObject({ a: 1 })); // true * isSignalObject({ a: 1 }); // false * isSignalObject({ ...signalObject({ a: 1 }) }); // false (spread = plain copy) */ declare function isSignalObject>(value: unknown): value is SignalObject; /** * Reactively merges multiple SignalObjects (like `{ ...a, ...b }`). * Returns a `Signal` that re-evaluates whenever any source property changes. * Later sources win on key conflicts, just like spread. * * same as `computed(() => ({ ...a, ...b }))` * but with proper tracking of nested properties and support for non-reactive objects as sources. * * @example * const objA = signalObject({ name: 'dvirus', role: 'dev' }); * const objB = signalObject({ age: 30, role: 'admin' }); * const merged = mergeSignalObjects(objA, objB); * merged(); // { name: 'dvirus', role: 'admin', age: 30 } */ declare function mergeSignalObjects(...sources: [...{ [K in keyof T]: T[K] | SignalObject; }]): Signal>; /** * Extracts the plain object type `U` from a `SignalObject`. * Returns `T` as-is if it's not a `SignalObject`. */ /** * Converts a union of types into an intersection. * Used to merge multiple object types from `mergeSignalObjects` into a single combined type. * * @example * // UnionToIntersection<{ a: 1 } | { b: 2 }> → { a: 1 } & { b: 2 } */ type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; interface ControlEvent { /** * Form control from which this event is originated. * * Note: the type of the control can't be inferred from T as the event can be emitted by any of child controls */ readonly source: AbstractControl; } /** * A reactive signal-based wrapper around an `AbstractControl`, exposing * the control's state (value, status, touched, dirty, errors, etc.) as * Angular signals for use in templates and computed expressions. * * @template T - The value type of the underlying form control. * @template TControl - The specific `AbstractControl` type being wrapped (e.g., `FormControl`, `FormGroup`, `FormArray`). */ interface ControlSignal = AbstractControl> { /** Signal returning the underlying `AbstractControl` instance with its specific type. */ control: TControl; /** Signal returning the current value of the control. */ value: Signal | null | undefined>; /** Signal returning the current validation status (`VALID`, `INVALID`, `PENDING`, `DISABLED`). */ status: Signal; /** Signal returning the most recent `ControlEvent` emitted by the control. */ events: Signal | null | undefined>; /** Signal that is `true` when the control status is `DISABLED`. */ disabled: Signal; /** Signal that is `true` when the control status is `VALID`. */ valid: Signal; /** Signal that is `true` when the control status is `INVALID`. */ invalid: Signal; /** Signal that is `true` when the control has been touched. */ touched: Signal; /** Signal that is `true` when the control is dirty. */ dirty: Signal; /** Signal returning the current validation errors, or `null` if there are none. */ errors: Signal; /** Signal returning the key of the first validation error, or `null`. */ firstErrorKey: Signal; /** Signal that is `true` when the control is both touched and invalid. */ touchedAndInvalid: Signal; /** * Signal returning the control's synchronous validators as a `ValidationErrors`-like object, or `null` if there are none. * @Note that this does not include asynchronous validators, and the shape of the returned object may differ from the actual validation errors emitted by the control. */ validators: Signal; /** Tears down all internal subscriptions and effects. */ unsubscribe(): void; } /** * Creates a {@link ControlSignal} that mirrors an `AbstractControl`'s * reactive state as Angular signals. * * **Important:** If called outside an injection context, you must manually handle * un-subscription by either: * - Calling the `unsubscribe()` method on the returned {@link ControlSignal}, or * - Passing a `DestroyRef` via the `options` parameter for automatic cleanup. * * If called within an injection context without providing a `DestroyRef`, the * subscriptions will be automatically cleaned up on component destruction. * * @template T - The value type of the form control. * @param control - The form control or a signal wrapping one. * @param options - Optional configuration object containing: * - `destroyRef`: A `DestroyRef` used for automatic cleanup on destruction. * If not provided, the function will attempt to inject one from the current * injection context. If neither is available, manual un-subscription is required. * @returns A {@link ControlSignal} exposing the control's state as signals. * @throws If `control` is a signal and no `Injector` is available. */ declare function controlSignal = FormControl>(control: TControl, options?: { destroyRef?: DestroyRef | null; }): ControlSignal; /** * Extends {@link ControlSignal} with a strongly-typed `controls` map, * providing a `ControlSignal` for every control in the `FormGroup`. * * @template T - An object type whose keys correspond to the group's control names * and whose values are the respective control value types. */ interface FormGroupSignal extends ControlSignal, FormGroup> { /** The underlying `FormGroup` instance. */ control: FormGroup; /** A map of child control names to their individual {@link ControlSignal} instances. */ controls: { [K in keyof TControls]: NestedControlSignal; }; } /** * Creates a {@link FormGroupSignal} for a `FormGroup`. Child controls are * converted recursively, so nested `FormGroup` and `FormArray` structures * are supported. * * @template TControls - A typed map of control names to `AbstractControl` instances. * @param formGroup - The `FormGroup` to wrap. * @param options - Optional `DestroyRef` and `Injector` forwarded to * each underlying {@link controlSignal} call. * @returns A {@link FormGroupSignal} with both group-level and per-control signals. */ declare function formGroupSignal(formGroup: FormGroup, options?: { destroyRef?: DestroyRef | null; }): FormGroupSignal; interface FormArraySignal> extends ControlSignal[], FormArray> { /** The underlying `FormArray` instance. */ control: FormArray; controls: NestedControlSignal[]; } /** * Creates a {@link FormArraySignal} for a `FormArray`. Child controls are * converted recursively, so arrays of `FormGroup`, arrays of `FormArray`, * and arrays of `FormControl` are all supported. */ declare function formArraySignal>(formArray: FormArray, options?: { destroyRef?: DestroyRef | null; }): FormArraySignal; type UnwrapFromControlState = T extends FormControlState ? T['value'] : T; type TypedControlMap = Record>; type ControlValue> = TControl extends AbstractControl ? TValue : never; type ControlsValue = { [K in keyof TControls]: ControlValue; }; type NestedControlSignal> = TControl extends FormGroup ? FormGroupSignal : TControl extends FormArray ? FormArraySignal> : ControlSignal, TControl>; /** * Types for the result tuple with discriminated union * @template T - Type of the successful result * @template E - Type of the error, defaults to Error */ type TryResult = [T, null] | [null, E]; /** * Main wrapper function to handle promise with try-catch * @template T - Type of the successful result * @template E - Type of the error, defaults to Error * @param {()=> T} fn - The function to handle * @returns {TryResult} - A tuple with either the result or the error */ declare function tryCatch(fn: () => T): TryResult; /** * Type for value equality comparison function */ type ValueEqualityFn = (a: T, b: T) => boolean; /** * Creates a `WritableSignal` whose value is derived from a computation function, * but can also be overridden manually via `.set()` / `.update()`. * When the reactive dependencies inside the computation change, the signal resets to the new derived value. * * Angular-16 compatible alternative to `linkedSignal` (Angular 19+). * * @overload * @param computation - A computation function that returns the derived value * @param options - Optional configuration (equal, debugName) * @returns A WritableSignal */ declare function writableSignal(computation: () => D, options?: { equal?: ValueEqualityFn; debugName?: string; }): WritableSignal; /** * Creates a `WritableSignal` with explicit source tracking and computation. * The `source` function provides the reactive dependencies, and the `computation` function derives the value from the source. * Manual overrides via `.set()` / `.update()` are only valid for the specific source state they were set against. * * Angular-16 compatible alternative to `linkedSignal` (Angular 19+). * * @overload * @param options - Configuration object with source tracking and computation * @returns A WritableSignal */ declare function writableSignal(options: { source: () => S; computation: (source: S, previous?: { source: S; value: D; }) => D; equal?: ValueEqualityFn; debugName?: string; }): WritableSignal; export { controlSignal, formArraySignal, formGroupSignal, fromSignalObj, isSignalObject, mergeSignalObjects, signalDebounce, signalMap, signalNotifier, signalObject, signalOrFunction, signalOrValue, signalSet, toSignalObj, tryCatch, writableSignal }; export type { ControlSignal, FormArraySignal, FormGroupSignal, NestedControlSignal, ObjectType, SignalDebounce, SignalMap, SignalNotifier, SignalObj, SignalObjWritable, SignalObject, SignalOrValue, SignalOrValueObj, SignalSet };