Skip to content

Commit

Permalink
fix(forms): Make UntypedFormBuilder assignable to FormBuilder, and vi…
Browse files Browse the repository at this point in the history
…ce versa. (angular#45421)

There was a subtle bug involving the opt-out class for FormBuilder, which I discovered during the ongoing migration. The types must be structurally the same, because people pass around FormBuilders, in addition to passing around the controls they produce. This PR ensures FormBuilder and UntypedFormBuilder are assignable to each other.

PR Close angular#45421
  • Loading branch information
dylhunn authored and PiyushAgrawal1243 committed Mar 30, 2022
1 parent 0f699d6 commit 6709ccf
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 18 deletions.
3 changes: 1 addition & 2 deletions goldens/public-api/forms/forms.md
Expand Up @@ -730,8 +730,7 @@ export type UntypedFormArray = FormArray;
export const UntypedFormArray: UntypedFormArrayCtor;

// @public
export class UntypedFormBuilder {
constructor();
export class UntypedFormBuilder extends FormBuilder {
// (undocumented)
array(controlsConfig: any[], validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null): UntypedFormArray;
// (undocumented)
Expand Down
24 changes: 9 additions & 15 deletions packages/forms/src/form_builder.ts
Expand Up @@ -187,49 +187,43 @@ export class FormBuilder {
* UntypedFormBuilder is the same as @see FormBuilder, but it provides untyped controls.
*/
@Injectable({providedIn: ReactiveFormsModule})
export class UntypedFormBuilder {
private _typedBuilder: FormBuilder;

constructor() {
this._typedBuilder = new FormBuilder();
}

export class UntypedFormBuilder extends FormBuilder {
/**
* @see FormBuilder#group
*/
group(
override group(
controlsConfig: {[key: string]: any},
options?: AbstractControlOptions|null,
): UntypedFormGroup;
/**
* @deprecated
*/
group(
override group(
controlsConfig: {[key: string]: any},
options: {[key: string]: any},
): UntypedFormGroup;
group(
override group(
controlsConfig: {[key: string]: any},
options: AbstractControlOptions|{[key: string]: any}|null = null): UntypedFormGroup {
return this._typedBuilder.group(controlsConfig, options);
return super.group(controlsConfig, options);
}

/**
* @see FormBuilder#control
*/
control(
override control(
formState: any, validatorOrOpts?: ValidatorFn|ValidatorFn[]|FormControlOptions|null,
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null): UntypedFormControl {
return this._typedBuilder.control(formState, validatorOrOpts, asyncValidator);
return super.control(formState, validatorOrOpts, asyncValidator);
}

/**
* @see FormBuilder#array
*/
array(
override array(
controlsConfig: any[],
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null): UntypedFormArray {
return this._typedBuilder.array(controlsConfig, validatorOrOpts, asyncValidator);
return super.array(controlsConfig, validatorOrOpts, asyncValidator);
}
}
15 changes: 14 additions & 1 deletion packages/forms/test/form_builder_spec.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {fakeAsync, tick} from '@angular/core/testing';
import {FormBuilder, Validators} from '@angular/forms';
import {FormBuilder, UntypedFormBuilder, Validators} from '@angular/forms';
import {of} from 'rxjs';

(function() {
Expand Down Expand Up @@ -276,4 +276,17 @@ describe('Form Builder', () => {
});
});
});

describe('UntypedFormBuilder', () => {
let fb: FormBuilder = new FormBuilder();
let ufb: UntypedFormBuilder = new UntypedFormBuilder();

function typedFn(fb: FormBuilder): void {}
function untypedFn(fb: UntypedFormBuilder): void {}

it('can be provided where a FormBuilder is expected and vice versa', () => {
typedFn(ufb);
untypedFn(fb);
});
});
})();

0 comments on commit 6709ccf

Please sign in to comment.