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

Code before:

/**
 * Helper type for defining `FieldPath`. Given a type `TValue` that may include `undefined`, it
 * extracts the `undefined` outside the `FieldPath` type.
 *
 * For example `MaybeFieldPath<{a: number} | undefined, PathKind.Child>` would be equivalent to
 * `undefined | FieldTree<{a: number}, PathKind.child>`.
 *
 * @template TValue The type of the data which the field is wrapped around.
 * @template TPathKind The kind of path (root field, child field, or item of an array)
 *
 * @experimental 21.0.0
 */
export type MaybeSchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> =
  | (TModel & undefined)
  | SchemaPathTree<Exclude<TModel, undefined>, TPathKind>;

/**
 * A precompiled, reusable schema that defines behavior and rules for a form.
 *
 * A `Schema` encapsulates form logic such as validation rules, disabled states, readonly states,
 * and other field-level behaviors.
 *
 * Unlike a raw {@link SchemaFn}, a `Schema` is created using
 * the {@link schema} function and is compiled only once, even when applied to multiple fields
 * or forms.
 *
 * ### Creating a reusable schema
 *
 * ```typescript
 * interface Address {
 *   street: string;
 *   city: string;
 * }
 *
 * // Create a reusable schema for address fields
 * const addressSchema = schema<Address>((p) => {
 *   required(p.street);
 *   required(p.city);
 * });
 *
 * // Apply the schema to multiple forms
 * const shippingForm = form(shippingModel, addressSchema, {injector});
 * const billingForm = form(billingModel, addressSchema, {injector});
 * ```
 *
 * ### Passing a schema to a form
 *
 * A schema can also be passed as a second argument to the {@link form} function.
 *
 * ```typescript

Review comment (security):
I'm not sure if this is quite right. It is compiled one per form, but I think across different forms it gets compiled separately - this is because the different forms have different injection context and we need to run the logic in the right context.

Code after:

/**
 * Helper type for defining `FieldPath`. Given a type `TValue` that may include `undefined`, it
 * extracts the `undefined` outside the `FieldPath` type.
 *
 * For example `MaybeFieldPath<{a: number} | undefined, PathKind.Child>` would be equivalent to
 * `undefined | FieldTree<{a: number}, PathKind.child>`.
 *
 * @template TValue The type of the data which the field is wrapped around.
 * @template TPathKind The kind of path (root field, child field, or item of an array)
 *
 * @experimental 21.0.0
 */
export type MaybeSchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> =
  | (TModel & undefined)
  | SchemaPathTree<Exclude<TModel, undefined>, TPathKind>;

/**
 * A reusable schema that defines behavior and rules for a form.
 *
 * A `Schema` encapsulates form logic such as validation rules, disabled states, readonly states,
 * and other field-level behaviors.
 *
 * Unlike raw {@link SchemaFn}, a `Schema` is created using
 * the {@link schema} function and is cached per-form, even when applied to multiple fields.
 *
 * ### Creating a reusable schema
 *
 * ```typescript
 * interface Address {
 *   street: string;
 *   city: string;
 * }
 *
 * // Create a reusable schema for address fields
 * const addressSchema = schema<Address>((p) => {
 *   required(p.street);
 *   required(p.city);
 * });
 *
 * // Apply the schema to multiple forms
 * const shippingForm = form(shippingModel, addressSchema, {injector});
 * const billingForm = form(billingModel, addressSchema, {injector});
 * ```
 *
 * ### Passing a schema to a form
 *
 * A schema can also be passed as a second argument to the {@link form} function.
 *
 * ```typescript
 * readonly userForm = form(addressModel, addressSchema);