ProxySignal
Creates a wrapper around a signal that intercepts get and set operations.
Usage
Provides explicit control over signal get (dependency tracking) and set (update triggering) operations, while preserving the standard WritableSignal or Signal interface.
Update logic customization enables you to define custom behavior for set() or update() calls on the created signal. For example, to implement custom scheduling:
import { signal } from '@angular/core';
import { proxySignal } from '@signality/core';
function debouncedSignal<T>(initialValue: T, ms: number): WritableSignal<T> {
const source = signal(initialValue);
let timeoutId: ReturnType<typeof setTimeout>;
return proxySignal(source, {
set: (value, source) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => source.set(value), ms);
}
});
}Now you can use debouncedSignal to create signals with delayed updates:
const search = debouncedSignal('', 300);
search.set('query'); // actual update happens after 300ms of inactivityNote
Signality uses proxySignal internally in utilities like Debounced and Throttled to implement scheduling behavior.
Parameters
| Parameter | Type | Description |
|---|---|---|
source | Signal<T> or WritableSignal<T> | Source signal to wrap |
handler | ProxySignalHandler<T, R> | Handler with optional get/set transformations |
options | Pick<CreateSignalOptions<R>, 'equal'> | Optional configuration (see Options below) |
Options
equal applies to the proxy handler, not the source signal
The equal option does not propagate to the original source signal. It is used exclusively by the proxy's set and update methods to compare transformed values — that is, the result of calling handler.get on the current source value.
By default, Object.is is used.
| Option | Type | Description |
|---|---|---|
equal | ValueEqualityFn<T> | Custom equality function |
Return Value
- When passed a writable signal → returns
WritableSignal<T> - When passed a readonly signal → returns
Signal<T>
Examples
Type transformation
Use get + set together for bidirectional transformation:
import { signal } from '@angular/core';
import { proxySignal } from '@signality/core';
const source = signal('a,b,c');
const proxy = proxySignal(source, {
get: s => s().split(','), // string → string[]
set: (v, s) => s.set(v.join(',')) // string[] → string
});
proxy(); // ['a', 'b', 'c']
proxy.set(['x', 'y']); // source becomes 'x,y'
proxy(); // ['x', 'y']Type Definitions
export type ProxySignalHandler<T, R = T> =
| { readonly get: (source: Signal<T>) => R; readonly set: (value: R, source: WritableSignal<T>) => void }
| { readonly get: (source: Signal<T>) => T; readonly set: (value: T, source: WritableSignal<T>) => void }
| { readonly get: (source: Signal<T>) => R; readonly set?: never }
| { readonly get?: never; readonly set: (value: T, source: WritableSignal<T>) => void }
| { readonly get?: never; readonly set?: never };
function proxySignal<T>(
source: WritableSignal<T>,
handler: { get: (source: Signal<T>) => T; set: (value: T, source: WritableSignal<T>) => void },
options?: Pick<CreateSignalOptions<T>, 'equal'>
): WritableSignal<T>;
function proxySignal<T, R>(
source: WritableSignal<T>,
handler: { get: (source: Signal<T>) => R; set: (value: R, source: WritableSignal<T>) => void },
options?: Pick<CreateSignalOptions<R>, 'equal'>
): WritableSignal<R>;
function proxySignal<T>(
source: WritableSignal<T>,
handler: { get: (source: Signal<T>) => T; set?: never },
options?: never
): WritableSignal<T>;
function proxySignal<T, R>(
source: Signal<T>,
handler: { get: (source: Signal<T>) => R; set?: never },
options?: never
): Signal<R>;
function proxySignal<T>(
source: WritableSignal<T>,
handler: { get?: never; set: (value: T, source: WritableSignal<T>) => void },
options?: Pick<CreateSignalOptions<T>, 'equal'>
): WritableSignal<T>;
function proxySignal<T>(
source: WritableSignal<T>,
handler: { get?: never; set?: never },
options?: never
): WritableSignal<T>;
function proxySignal<T>(
source: Signal<T>,
handler: { get?: never; set?: never },
options?: never
): Signal<T>;