mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(forms): Support readonly arrays in signal forms
This would allow using `readonly Array<...>` in types
This commit is contained in:
committed by
Kristiyan Kostadinov
parent
004813dc37
commit
282220d032
@@ -182,7 +182,7 @@ export interface FieldState<TValue, TKey extends string | number = string | numb
|
||||
}
|
||||
|
||||
// @public
|
||||
export type FieldTree<TModel, TKey extends string | number = string | number> = (() => [TModel] extends [AbstractControl] ? CompatFieldState<TModel, TKey> : FieldState<TModel, TKey>) & ([TModel] extends [AbstractControl] ? object : [TModel] extends [Array<infer U>] ? ReadonlyArrayLike<MaybeFieldTree<U, number>> : TModel extends Record<string, any> ? Subfields<TModel> : object);
|
||||
export type FieldTree<TModel, TKey extends string | number = string | number> = (() => [TModel] extends [AbstractControl] ? CompatFieldState<TModel, TKey> : FieldState<TModel, TKey>) & ([TModel] extends [AbstractControl] ? object : [TModel] extends [ReadonlyArray<infer U>] ? ReadonlyArrayLike<MaybeFieldTree<U, number>> : TModel extends Record<string, any> ? Subfields<TModel> : object);
|
||||
|
||||
// @public
|
||||
export type FieldValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult<ValidationError.WithoutField>, TPathKind>;
|
||||
@@ -513,7 +513,7 @@ export namespace SchemaPathRules {
|
||||
}
|
||||
|
||||
// @public
|
||||
export type SchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> = ([TModel] extends [AbstractControl] ? CompatSchemaPath<TModel, TPathKind> : SchemaPath<TModel, SchemaPathRules.Supported, TPathKind>) & (TModel extends AbstractControl ? unknown : TModel extends Array<any> ? unknown : TModel extends Record<string, any> ? {
|
||||
export type SchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> = ([TModel] extends [AbstractControl] ? CompatSchemaPath<TModel, TPathKind> : SchemaPath<TModel, SchemaPathRules.Supported, TPathKind>) & (TModel extends AbstractControl ? unknown : TModel extends ReadonlyArray<any> ? unknown : TModel extends Record<string, any> ? {
|
||||
[K in keyof TModel]: MaybeSchemaPathTree<TModel[K], PathKind.Child>;
|
||||
} : unknown);
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
import {Signal, ɵFieldState} from '@angular/core';
|
||||
import {AbstractControl} from '@angular/forms';
|
||||
import type {Field} from './field_directive';
|
||||
import type {MetadataKey} from './rules/metadata';
|
||||
import type {ValidationError} from './rules/validation/validation_errors';
|
||||
import type {ValidationError, MetadataKey} from './rules';
|
||||
|
||||
/**
|
||||
* Symbol used to retain generic type information when it would otherwise be lost.
|
||||
@@ -169,7 +168,7 @@ export type FieldTree<TModel, TKey extends string | number = string | number> =
|
||||
// Children:
|
||||
([TModel] extends [AbstractControl]
|
||||
? object
|
||||
: [TModel] extends [Array<infer U>]
|
||||
: [TModel] extends [ReadonlyArray<infer U>]
|
||||
? ReadonlyArrayLike<MaybeFieldTree<U, number>>
|
||||
: TModel extends Record<string, any>
|
||||
? Subfields<TModel>
|
||||
@@ -408,7 +407,7 @@ export type SchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> =
|
||||
(TModel extends AbstractControl
|
||||
? unknown
|
||||
: // Array paths have no subpaths
|
||||
TModel extends Array<any>
|
||||
TModel extends ReadonlyArray<any>
|
||||
? unknown
|
||||
: // Object subfields
|
||||
TModel extends Record<string, any>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {signal, WritableSignal} from '@angular/core';
|
||||
import {form, required, schema, SchemaFn} from '../../public_api';
|
||||
import {FieldTree, form, required, schema, SchemaFn} from '../../public_api';
|
||||
|
||||
interface Order {
|
||||
id: string;
|
||||
@@ -87,5 +87,30 @@ function typeVerificationOnlyDoNotRunMe() {
|
||||
type RecursiveType = (number | RecursiveType)[];
|
||||
form(signal<RecursiveType>([5]));
|
||||
});
|
||||
|
||||
it('should allow ReadonlyArray in model and be iterable', () => {
|
||||
interface Order {
|
||||
readonly products: readonly string[];
|
||||
}
|
||||
const order: WritableSignal<Order> = null!;
|
||||
const f = form(order);
|
||||
// Iterating over products should yield FieldTree<string> items, not [string, FieldTree] entries
|
||||
for (const product of f.products) {
|
||||
const p: FieldTree<string> = product;
|
||||
p().value();
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow Array in model and be iterable', () => {
|
||||
interface Order {
|
||||
products: string[];
|
||||
}
|
||||
const order: WritableSignal<Order> = null!;
|
||||
const f = form(order);
|
||||
for (const product of f.products) {
|
||||
const p: FieldTree<string> = product;
|
||||
p().value();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user