mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(forms): Value and RawValue should be part of the public API. (#45978)
Consider a typed group for storing contact information:
```
declare interface ContactControls {
name: FormControl<string|null>;
}
contactForm: FormGroup<ContactControls> = ...;
saveForm(form: FormGroup<ContactControls>) {
service.newContact(contactForm.value);
}
```
What should be the type of `newContact`? The answer, of course, is the value type:
```
declare interface Contact {
name: string|null;
}
class ContactService {
newContact(c: Contact) {}
}
```
This is quite redundant, and therefore, we should allow the value type to be generated automatically. We already have the helper types to do this -- we just need to document and export them. Then, this becomes possible:
```
class ContactService {
newContact(c: RawValue<FormGroup<ContactControls>>) {}
}
```
PR Close #45978
This commit is contained in:
committed by
Jessica Janiuk
parent
4070049d8a
commit
dba6a60861
@@ -494,7 +494,7 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit,
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class FormRecord<TControl extends AbstractControl<ɵValue<TControl>, ɵRawValue<TControl>> = AbstractControl> extends FormGroup<{
|
||||
export class FormRecord<TControl extends AbstractControl<Value<TControl>, RawValue<TControl>> = AbstractControl> extends FormGroup<{
|
||||
[key: string]: TControl;
|
||||
}> {
|
||||
}
|
||||
@@ -506,10 +506,10 @@ export interface FormRecord<TControl> {
|
||||
}): void;
|
||||
contains(controlName: string): boolean;
|
||||
getRawValue(): {
|
||||
[key: string]: ɵRawValue<TControl>;
|
||||
[key: string]: RawValue<TControl>;
|
||||
};
|
||||
patchValue(value: {
|
||||
[key: string]: ɵValue<TControl>;
|
||||
[key: string]: Value<TControl>;
|
||||
}, options?: {
|
||||
onlySelf?: boolean;
|
||||
emitEvent?: boolean;
|
||||
@@ -519,7 +519,7 @@ export interface FormRecord<TControl> {
|
||||
emitEvent?: boolean;
|
||||
}): void;
|
||||
reset(value?: {
|
||||
[key: string]: ɵValue<TControl>;
|
||||
[key: string]: Value<TControl>;
|
||||
}, options?: {
|
||||
onlySelf?: boolean;
|
||||
emitEvent?: boolean;
|
||||
@@ -528,7 +528,7 @@ export interface FormRecord<TControl> {
|
||||
emitEvent?: boolean;
|
||||
}): void;
|
||||
setValue(value: {
|
||||
[key: string]: ɵValue<TControl>;
|
||||
[key: string]: Value<TControl>;
|
||||
}, options?: {
|
||||
onlySelf?: boolean;
|
||||
emitEvent?: boolean;
|
||||
@@ -767,6 +767,9 @@ export class RangeValueAccessor extends BuiltInControlValueAccessor implements C
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<RangeValueAccessor, never>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type RawValue<T extends AbstractControl | undefined> = T extends AbstractControl<any, any> ? (T['setValue'] extends ((v: infer R) => void) ? R : never) : never;
|
||||
|
||||
// @public
|
||||
export class ReactiveFormsModule {
|
||||
static withConfig(opts: {
|
||||
@@ -887,6 +890,9 @@ export class Validators {
|
||||
static requiredTrue(control: AbstractControl): ValidationErrors | null;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type Value<T extends AbstractControl | undefined> = T extends AbstractControl<any, any> ? T['value'] : never;
|
||||
|
||||
// @public (undocumented)
|
||||
export const VERSION: Version;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export {NgSelectOption, SelectControlValueAccessor} from './directives/select_co
|
||||
export {SelectMultipleControlValueAccessor, ɵNgSelectMultipleOption} from './directives/select_multiple_control_value_accessor';
|
||||
export {AsyncValidator, AsyncValidatorFn, CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, PatternValidator, RequiredValidator, ValidationErrors, Validator, ValidatorFn} from './directives/validators';
|
||||
export {FormBuilder, NonNullableFormBuilder, UntypedFormBuilder, ɵElement} from './form_builder';
|
||||
export {AbstractControl, AbstractControlOptions, FormControlStatus, ɵCoerceStrArrToNumArr, ɵGetProperty, ɵNavigate, ɵRawValue, ɵTokenize, ɵTypedOrUntyped, ɵValue, ɵWriteable} from './model/abstract_model';
|
||||
export {AbstractControl, AbstractControlOptions, FormControlStatus, RawValue, Value, ɵCoerceStrArrToNumArr, ɵGetProperty, ɵNavigate, ɵTokenize, ɵTypedOrUntyped, ɵWriteable} from './model/abstract_model';
|
||||
export {FormArray, UntypedFormArray, ɵFormArrayRawValue, ɵFormArrayValue} from './model/form_array';
|
||||
export {FormControl, FormControlOptions, FormControlState, UntypedFormControl, ɵFormControlCtor} from './model/form_control';
|
||||
export {FormGroup, FormRecord, UntypedFormGroup, ɵFormGroupRawValue, ɵFormGroupValue, ɵOptionalKeys} from './model/form_group';
|
||||
|
||||
@@ -166,19 +166,86 @@ export type ɵIsAny<T, Y, N> = 0 extends(1&T) ? Y : N;
|
||||
export type ɵTypedOrUntyped<T, Typed, Untyped> = ɵIsAny<T, Untyped, Typed>;
|
||||
|
||||
/**
|
||||
* Value gives the type of `.value` in an `AbstractControl`.
|
||||
* Value gives the value type corresponding to a control type.
|
||||
*
|
||||
* For internal use only.
|
||||
* Note that the resulting type will follow the same rules as `.value` on your control, group, or
|
||||
* array, including `undefined` for each group element which might be disabled.
|
||||
*
|
||||
* If you are trying to extract a value type for a data model, you probably want {@see RawValue},
|
||||
* which will not have `undefined` in group keys.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* ### `FormControl` value type
|
||||
*
|
||||
* You can extract the value type of a single control:
|
||||
*
|
||||
* ```ts
|
||||
* type NameControl = FormControl<string>;
|
||||
* type NameValue = Value<NameControl>;
|
||||
* ```
|
||||
*
|
||||
* The resulting type is `string`.
|
||||
*
|
||||
* ### `FormGroup` value type
|
||||
*
|
||||
* Imagine you have an interface defining the controls in your group. You can extract the shape of
|
||||
* the values as follows:
|
||||
*
|
||||
* ```ts
|
||||
* interface PartyFormControls {
|
||||
* address: FormControl<string>;
|
||||
* }
|
||||
*
|
||||
* // Value operates on controls; the object must be wrapped in a FormGroup.
|
||||
* type PartyFormValues = Value<FormGroup<PartyFormControls>>;
|
||||
* ```
|
||||
*
|
||||
* The resulting type is `{address: string|undefined}`.
|
||||
*
|
||||
* ### `FormArray` value type
|
||||
*
|
||||
* You can extract values from FormArrays as well:
|
||||
*
|
||||
* ```ts
|
||||
* type GuestNamesControls = FormArray<FormControl<string>>;
|
||||
*
|
||||
* type NamesValues = Value<GuestNamesControls>;
|
||||
* ```
|
||||
*
|
||||
* The resulting type is `string[]`.
|
||||
*/
|
||||
export type ɵValue<T extends AbstractControl|undefined> =
|
||||
export type Value<T extends AbstractControl|undefined> =
|
||||
T extends AbstractControl<any, any>? T['value'] : never;
|
||||
|
||||
/**
|
||||
* RawValue gives the type of `.getRawValue()` in an `AbstractControl`.
|
||||
* RawValue gives the raw value type corresponding to a control type.
|
||||
*
|
||||
* For internal use only.
|
||||
* Note that the resulting type will follow the same rules as `.getRawValue()` on your control,
|
||||
* group, or array. This means that all controls inside a group will be required, not optional,
|
||||
* regardless of their disabled state.
|
||||
*
|
||||
* You may also wish to use {@see Value}, which will have `undefined` in group keys (which can be disabled).
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* ### `FormGroup` raw value type
|
||||
*
|
||||
* Imagine you have an interface defining the controls in your group. You can extract the shape of
|
||||
* the raw values as follows:
|
||||
*
|
||||
* ```ts
|
||||
* interface PartyFormControls {
|
||||
* address: FormControl<string>;
|
||||
* }
|
||||
*
|
||||
* // RawValue operates on controls; the object must be wrapped in a FormGroup.
|
||||
* type PartyFormValues = RawValue<FormGroup<PartyFormControls>>;
|
||||
* ```
|
||||
*
|
||||
* The resulting type is `{address: string}`. (Note the absence of `undefined`.)
|
||||
*/
|
||||
export type ɵRawValue<T extends AbstractControl|undefined> = T extends AbstractControl<any, any>?
|
||||
export type RawValue<T extends AbstractControl|undefined> = T extends AbstractControl<any, any>?
|
||||
(T['setValue'] extends((v: infer R) => void) ? R : never) :
|
||||
never;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from '../directives/validators';
|
||||
|
||||
import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertControlPresent, pickAsyncValidators, pickValidators, ɵRawValue, ɵTypedOrUntyped, ɵValue} from './abstract_model';
|
||||
import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertControlPresent, pickAsyncValidators, pickValidators, RawValue, Value, ɵTypedOrUntyped} from './abstract_model';
|
||||
|
||||
/**
|
||||
* FormArrayValue extracts the type of `.value` from a FormArray's element type, and wraps it in an
|
||||
@@ -18,7 +18,7 @@ import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertC
|
||||
* case falls back to any[].
|
||||
*/
|
||||
export type ɵFormArrayValue<T extends AbstractControl<any>> =
|
||||
ɵTypedOrUntyped<T, Array<ɵValue<T>>, any[]>;
|
||||
ɵTypedOrUntyped<T, Array<Value<T>>, any[]>;
|
||||
|
||||
/**
|
||||
* FormArrayRawValue extracts the type of `.getRawValue()` from a FormArray's element type, and
|
||||
@@ -27,7 +27,7 @@ export type ɵFormArrayValue<T extends AbstractControl<any>> =
|
||||
* Angular uses this type internally to support Typed Forms; do not use it directly.
|
||||
*/
|
||||
export type ɵFormArrayRawValue<T extends AbstractControl<any>> =
|
||||
ɵTypedOrUntyped<T, Array<ɵRawValue<T>>, any[]>;
|
||||
ɵTypedOrUntyped<T, Array<RawValue<T>>, any[]>;
|
||||
|
||||
/**
|
||||
* Tracks the value and validity state of an array of `FormControl`,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from '../directives/validators';
|
||||
|
||||
import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertControlPresent, pickAsyncValidators, pickValidators, ɵRawValue, ɵTypedOrUntyped, ɵValue} from './abstract_model';
|
||||
import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertControlPresent, pickAsyncValidators, pickValidators, RawValue, Value, ɵTypedOrUntyped} from './abstract_model';
|
||||
|
||||
/**
|
||||
* FormGroupValue extracts the type of `.value` from a FormGroup's inner object type. The untyped
|
||||
@@ -19,7 +19,7 @@ import {AbstractControl, AbstractControlOptions, assertAllValuesPresent, assertC
|
||||
* For internal use only.
|
||||
*/
|
||||
export type ɵFormGroupValue<T extends {[K in keyof T]?: AbstractControl<any>}> =
|
||||
ɵTypedOrUntyped<T, Partial<{[K in keyof T]: ɵValue<T[K]>}>, {[key: string]: any}>;
|
||||
ɵTypedOrUntyped<T, Partial<{[K in keyof T]: Value<T[K]>}>, {[key: string]: any}>;
|
||||
|
||||
/**
|
||||
* FormGroupRawValue extracts the type of `.getRawValue()` from a FormGroup's inner object type. The
|
||||
@@ -30,7 +30,7 @@ export type ɵFormGroupValue<T extends {[K in keyof T]?: AbstractControl<any>}>
|
||||
* For internal use only.
|
||||
*/
|
||||
export type ɵFormGroupRawValue<T extends {[K in keyof T]?: AbstractControl<any>}> =
|
||||
ɵTypedOrUntyped<T, {[K in keyof T]: ɵRawValue<T[K]>}, {[key: string]: any}>;
|
||||
ɵTypedOrUntyped<T, {[K in keyof T]: RawValue<T[K]>}, {[key: string]: any}>;
|
||||
|
||||
/**
|
||||
* OptionalKeys returns the union of all optional keys in the object.
|
||||
@@ -606,7 +606,7 @@ export const UntypedFormGroup: UntypedFormGroupCtor = FormGroup;
|
||||
|
||||
export const isFormGroup = (control: unknown): control is FormGroup => control instanceof FormGroup;
|
||||
|
||||
export class FormRecord<TControl extends AbstractControl<ɵValue<TControl>, ɵRawValue<TControl>> =
|
||||
export class FormRecord<TControl extends AbstractControl<Value<TControl>, RawValue<TControl>> =
|
||||
AbstractControl> extends
|
||||
FormGroup<{[key: string]: TControl}> {}
|
||||
|
||||
@@ -671,7 +671,7 @@ export interface FormRecord<TControl> {
|
||||
*
|
||||
* {@see FormGroup#setValue}
|
||||
*/
|
||||
setValue(value: {[key: string]: ɵValue<TControl>}, options?: {
|
||||
setValue(value: {[key: string]: Value<TControl>}, options?: {
|
||||
onlySelf?: boolean,
|
||||
emitEvent?: boolean
|
||||
}): void;
|
||||
@@ -683,7 +683,7 @@ export interface FormRecord<TControl> {
|
||||
*
|
||||
* {@see FormGroup#patchValue}
|
||||
*/
|
||||
patchValue(value: {[key: string]: ɵValue<TControl>}, options?: {
|
||||
patchValue(value: {[key: string]: Value<TControl>}, options?: {
|
||||
onlySelf?: boolean,
|
||||
emitEvent?: boolean
|
||||
}): void;
|
||||
@@ -694,7 +694,7 @@ export interface FormRecord<TControl> {
|
||||
*
|
||||
* {@see FormGroup#reset}
|
||||
*/
|
||||
reset(value?: {[key: string]: ɵValue<TControl>}, options?: {
|
||||
reset(value?: {[key: string]: Value<TControl>}, options?: {
|
||||
onlySelf?: boolean,
|
||||
emitEvent?: boolean
|
||||
}): void;
|
||||
@@ -704,7 +704,7 @@ export interface FormRecord<TControl> {
|
||||
*
|
||||
* {@see FormGroup#getRawValue}
|
||||
*/
|
||||
getRawValue(): {[key: string]: ɵRawValue<TControl>};
|
||||
getRawValue(): {[key: string]: RawValue<TControl>};
|
||||
}
|
||||
|
||||
export const isFormRecord = (control: unknown): control is FormRecord =>
|
||||
|
||||
Reference in New Issue
Block a user