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. (#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 #45421
  • Loading branch information
dylhunn committed Mar 24, 2022
1 parent 38ad849 commit fe0e42a
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 fe0e42a

Please sign in to comment.