Repository: angular/angular
File: packages/forms/signals/src/api/transformed_value.ts (TypeScript)

Code before:
  format: (value: TValue) => TRaw;
}

/**
 * A writable signal representing a "raw" UI value that is synchronized with a model signal
 * via parse/format transformations.
 *
 * @category control
 * @experimental 21.2.0
 */
export interface TransformedValueSignal<TRaw> extends WritableSignal<TRaw> {
  /**
   * The current parse errors resulting from the last transformation.
   */
  readonly parseErrors: Signal<readonly ValidationError.WithoutFieldTree[]>;
}

/**
 * Creates a writable signal representing a "raw" UI value that is transformed to/from a model
 * value via `parse` and `format` functions.
 *
 * This utility simplifies the creation of custom form controls that parse a user-facing value
 * representation into an underlying model value. For example, a numeric input that displays and
 * accepts string values but stores a number.
 *
 * Parse errors are exposed via `rawValue.parseErrors()`. When `transformedValue` is used within a
 * Signal Forms field context, parse errors are also reported to the nearest field automatically.
 * When no field context is present, no automatic reporting occurs and `parseErrors` can be consumed
 * directly.
 *
 * Note: `parse` may return both a `value` and an `error`. Returning `value` updates the model; omitting
 * it leaves the model unchanged.
 *
 * @param value The model signal to synchronize with.
 * @param options Configuration including `parse` and `format` functions.
 * @returns A `TransformedValueSignal` representing the raw value with parse error tracking.
 * @experimental 21.2.0
 *
 * @example
 * ```ts
 * @Component({
 *   selector: 'number-input',
 *   template: `<input [value]="rawValue()" (input)="rawValue.set($event.target.value)" />`,
 * })
 * export class NumberInput implements FormValueControl<number | null> {
 *   readonly value = model.required<number | null>();
 *
 *   protected readonly rawValue = transformedValue(this.value, {
 *     parse: (val) => {
 *       if (val === '') return {value: null};
 *       const num = Number(val);

Review comment (suggestion):
The use of `rawValue` isn't really clear. We should probably explicit that this is done on the return value of `transformedValue`

Code after:
  format: (value: TValue) => TRaw;
}

/**
 * A writable signal representing a "raw" UI value that is synchronized with a model signal
 * via parse/format transformations.
 *
 * @category control
 * @experimental 21.2.0
 */
export interface TransformedValueSignal<TRaw> extends WritableSignal<TRaw> {
  /**
   * The current parse errors resulting from the last transformation.
   */
  readonly parseErrors: Signal<readonly ValidationError.WithoutFieldTree[]>;
}

/**
 * Creates a writable signal representing a "raw" UI value that is transformed to/from a model
 * value via `parse` and `format` functions.
 *
 * This utility simplifies the creation of custom form controls that parse a user-facing value
 * representation into an underlying model value. For example, a numeric input that displays and
 * accepts string values but stores a number.
 *
 * Parse errors are exposed via the returned signal’s `parseErrors()` property.
 * When `transformedValue` is used within a Signal Forms field context, parse errors are also
 * reported to the nearest field automatically. When no field context is present, no automatic
 * reporting occurs and `parseErrors` can be consumed directly.
 *
 * Note: `parse` may return both a `value` and an `error`. Returning `value` updates the model;
 * omitting it leaves the model unchanged.
 *
 * @param value The model signal to synchronize with.
 * @param options Configuration including `parse` and `format` functions.
 * @returns A `TransformedValueSignal` representing the raw value with parse error tracking.
 * @experimental 21.2.0
 *
 * @example
 * ```ts
 * @Component({
 *   selector: 'number-input',
 *   template: `<input [value]="rawValue()" (input)="rawValue.set($event.target.value)" />`,
 * })
 * export class NumberInput implements FormValueControl<number | null> {
 *   readonly value = model.required<number | null>();
 *
 *   protected readonly rawValue = transformedValue(this.value, {
 *     parse: (val) => {
 *       if (val === '') return {value: null};
 *       const num = Number(val);