From 6a2e08d0a8ae24b73d457d58d1b224ce1487ffac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Thu, 13 Apr 2017 17:14:08 -0700 Subject: [PATCH] fix(forms): Update types for TypeScript nullability support (#15859) --- .../directives/abstract_control_directive.ts | 34 +++---- .../abstract_form_group_directive.ts | 12 +-- .../forms/src/directives/control_container.ts | 6 +- packages/forms/src/directives/ng_control.ts | 10 +-- packages/forms/src/directives/ng_form.ts | 2 +- packages/forms/src/directives/ng_model.ts | 6 +- .../src/directives/number_value_accessor.ts | 2 +- .../src/directives/range_value_accessor.ts | 2 +- .../form_control_directive.ts | 8 +- .../reactive_directives/form_control_name.ts | 10 +-- .../form_group_directive.ts | 6 +- .../reactive_directives/form_group_name.ts | 12 +-- .../select_control_value_accessor.ts | 6 +- .../select_multiple_control_value_accessor.ts | 6 +- packages/forms/src/directives/shared.ts | 45 +++++----- packages/forms/src/form_builder.ts | 10 +-- packages/forms/src/model.ts | 47 +++++----- packages/forms/src/validators.ts | 12 +-- packages/forms/test/directives_spec.ts | 38 ++++---- packages/forms/test/form_array_spec.ts | 48 +++++----- packages/forms/test/form_control_spec.ts | 29 +++--- packages/forms/test/form_group_spec.ts | 56 ++++++------ .../forms/test/reactive_integration_spec.ts | 52 +++++------ .../forms/test/template_integration_spec.ts | 66 +++++++------- packages/forms/test/validators_spec.ts | 58 ++++++------ packages/forms/tsconfig-build.json | 1 + tools/public_api_guard/forms/forms.d.ts | 89 ++++++++++--------- 27 files changed, 343 insertions(+), 330 deletions(-) diff --git a/packages/forms/src/directives/abstract_control_directive.ts b/packages/forms/src/directives/abstract_control_directive.ts index f2cef2e96f..1bf7524dae 100644 --- a/packages/forms/src/directives/abstract_control_directive.ts +++ b/packages/forms/src/directives/abstract_control_directive.ts @@ -18,45 +18,49 @@ import {ValidationErrors} from './validators'; * @stable */ export abstract class AbstractControlDirective { - get control(): AbstractControl { throw new Error('unimplemented'); } + abstract get control(): AbstractControl|null; get value(): any { return this.control ? this.control.value : null; } - get valid(): boolean { return this.control ? this.control.valid : null; } + get valid(): boolean|null { return this.control ? this.control.valid : null; } - get invalid(): boolean { return this.control ? this.control.invalid : null; } + get invalid(): boolean|null { return this.control ? this.control.invalid : null; } - get pending(): boolean { return this.control ? this.control.pending : null; } + get pending(): boolean|null { return this.control ? this.control.pending : null; } get errors(): ValidationErrors|null { return this.control ? this.control.errors : null; } - get pristine(): boolean { return this.control ? this.control.pristine : null; } + get pristine(): boolean|null { return this.control ? this.control.pristine : null; } - get dirty(): boolean { return this.control ? this.control.dirty : null; } + get dirty(): boolean|null { return this.control ? this.control.dirty : null; } - get touched(): boolean { return this.control ? this.control.touched : null; } + get touched(): boolean|null { return this.control ? this.control.touched : null; } - get untouched(): boolean { return this.control ? this.control.untouched : null; } + get untouched(): boolean|null { return this.control ? this.control.untouched : null; } - get disabled(): boolean { return this.control ? this.control.disabled : null; } + get disabled(): boolean|null { return this.control ? this.control.disabled : null; } - get enabled(): boolean { return this.control ? this.control.enabled : null; } + get enabled(): boolean|null { return this.control ? this.control.enabled : null; } - get statusChanges(): Observable { return this.control ? this.control.statusChanges : null; } + get statusChanges(): Observable|null { + return this.control ? this.control.statusChanges : null; + } - get valueChanges(): Observable { return this.control ? this.control.valueChanges : null; } + get valueChanges(): Observable|null { + return this.control ? this.control.valueChanges : null; + } - get path(): string[] { return null; } + get path(): string[]|null { return null; } reset(value: any = undefined): void { if (this.control) this.control.reset(value); } - hasError(errorCode: string, path: string[] = null): boolean { + hasError(errorCode: string, path?: string[]): boolean { return this.control ? this.control.hasError(errorCode, path) : false; } - getError(errorCode: string, path: string[] = null): any { + getError(errorCode: string, path?: string[]): any { return this.control ? this.control.getError(errorCode, path) : null; } } diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index 19661ec147..30829df9f2 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -34,7 +34,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn ngOnInit(): void { this._checkParentType(); - this.formDirective.addFormGroup(this); + this.formDirective !.addFormGroup(this); } ngOnDestroy(): void { @@ -46,7 +46,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn /** * Get the {@link FormGroup} backing this binding. */ - get control(): FormGroup { return this.formDirective.getFormGroup(this); } + get control(): FormGroup { return this.formDirective !.getFormGroup(this); } /** * Get the path to this control group. @@ -56,11 +56,13 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn /** * Get the {@link Form} to which this group belongs. */ - get formDirective(): Form { return this._parent ? this._parent.formDirective : null; } + get formDirective(): Form|null { return this._parent ? this._parent.formDirective : null; } - get validator(): ValidatorFn { return composeValidators(this._validators); } + get validator(): ValidatorFn|null { return composeValidators(this._validators); } - get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); } + get asyncValidator(): AsyncValidatorFn|null { + return composeAsyncValidators(this._asyncValidators); + } /** @internal */ _checkParentType(): void {} diff --git a/packages/forms/src/directives/control_container.ts b/packages/forms/src/directives/control_container.ts index a0ee276426..39225c0dca 100644 --- a/packages/forms/src/directives/control_container.ts +++ b/packages/forms/src/directives/control_container.ts @@ -17,16 +17,16 @@ import {Form} from './form_interface'; * * @stable */ -export class ControlContainer extends AbstractControlDirective { +export abstract class ControlContainer extends AbstractControlDirective { name: string; /** * Get the form to which this container belongs. */ - get formDirective(): Form { return null; } + get formDirective(): Form|null { return null; } /** * Get the path to this container. */ - get path(): string[] { return null; } + get path(): string[]|null { return null; } } diff --git a/packages/forms/src/directives/ng_control.ts b/packages/forms/src/directives/ng_control.ts index 7f2d4b04cc..503adf9281 100644 --- a/packages/forms/src/directives/ng_control.ts +++ b/packages/forms/src/directives/ng_control.ts @@ -26,16 +26,16 @@ function unimplemented(): any { */ export abstract class NgControl extends AbstractControlDirective { /** @internal */ - _parent: ControlContainer = null; - name: string = null; - valueAccessor: ControlValueAccessor = null; + _parent: ControlContainer|null = null; + name: string|null = null; + valueAccessor: ControlValueAccessor|null = null; /** @internal */ _rawValidators: Array = []; /** @internal */ _rawAsyncValidators: Array = []; - get validator(): ValidatorFn { return unimplemented(); } - get asyncValidator(): AsyncValidatorFn { return unimplemented(); } + get validator(): ValidatorFn|null { return unimplemented(); } + get asyncValidator(): AsyncValidatorFn|null { return unimplemented(); } abstract viewToModelUpdate(newValue: any): void; } diff --git a/packages/forms/src/directives/ng_form.ts b/packages/forms/src/directives/ng_form.ts index 6803a6d296..6f69007d69 100644 --- a/packages/forms/src/directives/ng_form.ts +++ b/packages/forms/src/directives/ng_form.ts @@ -130,7 +130,7 @@ export class NgForm extends ControlContainer implements Form { updateModel(dir: NgControl, value: any): void { resolvedPromise.then(() => { - const ctrl = this.form.get(dir.path); + const ctrl = this.form.get(dir.path !); ctrl.setValue(value); }); } diff --git a/packages/forms/src/directives/ng_model.ts b/packages/forms/src/directives/ng_model.ts index c27b28d026..7d58e51e80 100644 --- a/packages/forms/src/directives/ng_model.ts +++ b/packages/forms/src/directives/ng_model.ts @@ -158,9 +158,9 @@ export class NgModel extends NgControl implements OnChanges, get formDirective(): any { return this._parent ? this._parent.formDirective : null; } - get validator(): ValidatorFn { return composeValidators(this._rawValidators); } + get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } - get asyncValidator(): AsyncValidatorFn { + get asyncValidator(): AsyncValidatorFn|null { return composeAsyncValidators(this._rawAsyncValidators); } @@ -176,7 +176,7 @@ export class NgModel extends NgControl implements OnChanges, } private _isStandalone(): boolean { - return !this._parent || (this.options && this.options.standalone); + return !this._parent || !!(this.options && this.options.standalone); } private _setUpStandalone(): void { diff --git a/packages/forms/src/directives/number_value_accessor.ts b/packages/forms/src/directives/number_value_accessor.ts index 8c4baf8433..3710faa248 100644 --- a/packages/forms/src/directives/number_value_accessor.ts +++ b/packages/forms/src/directives/number_value_accessor.ts @@ -47,7 +47,7 @@ export class NumberValueAccessor implements ControlValueAccessor { this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue); } - registerOnChange(fn: (_: number) => void): void { + registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; } registerOnTouched(fn: () => void): void { this.onTouched = fn; } diff --git a/packages/forms/src/directives/range_value_accessor.ts b/packages/forms/src/directives/range_value_accessor.ts index cf89bc920c..01b5dc0ff3 100644 --- a/packages/forms/src/directives/range_value_accessor.ts +++ b/packages/forms/src/directives/range_value_accessor.ts @@ -45,7 +45,7 @@ export class RangeValueAccessor implements ControlValueAccessor { this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', parseFloat(value)); } - registerOnChange(fn: (_: number) => void): void { + registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; } diff --git a/packages/forms/src/directives/reactive_directives/form_control_directive.ts b/packages/forms/src/directives/reactive_directives/form_control_directive.ts index 9db23e2aeb..eefabe0702 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_directive.ts @@ -88,8 +88,8 @@ export class FormControlDirective extends NgControl implements OnChanges { ngOnChanges(changes: SimpleChanges): void { if (this._isControlChanged(changes)) { setUpControl(this.form, this); - if (this.control.disabled && this.valueAccessor.setDisabledState) { - this.valueAccessor.setDisabledState(true); + if (this.control.disabled && this.valueAccessor !.setDisabledState) { + this.valueAccessor !.setDisabledState !(true); } this.form.updateValueAndValidity({emitEvent: false}); } @@ -101,9 +101,9 @@ export class FormControlDirective extends NgControl implements OnChanges { get path(): string[] { return []; } - get validator(): ValidatorFn { return composeValidators(this._rawValidators); } + get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } - get asyncValidator(): AsyncValidatorFn { + get asyncValidator(): AsyncValidatorFn|null { return composeAsyncValidators(this._rawAsyncValidators); } diff --git a/packages/forms/src/directives/reactive_directives/form_control_name.ts b/packages/forms/src/directives/reactive_directives/form_control_name.ts index def1aeb898..b4e06d4a5c 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_name.ts @@ -125,14 +125,14 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { this.update.emit(newValue); } - get path(): string[] { return controlPath(this.name, this._parent); } + get path(): string[] { return controlPath(this.name, this._parent !); } get formDirective(): any { return this._parent ? this._parent.formDirective : null; } - get validator(): ValidatorFn { return composeValidators(this._rawValidators); } + get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } get asyncValidator(): AsyncValidatorFn { - return composeAsyncValidators(this._rawAsyncValidators); + return composeAsyncValidators(this._rawAsyncValidators) !; } get control(): FormControl { return this._control; } @@ -151,8 +151,8 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { private _setUpControl() { this._checkParentType(); this._control = this.formDirective.addControl(this); - if (this.control.disabled && this.valueAccessor.setDisabledState) { - this.valueAccessor.setDisabledState(true); + if (this.control.disabled && this.valueAccessor !.setDisabledState) { + this.valueAccessor !.setDisabledState !(true); } this._added = true; } diff --git a/packages/forms/src/directives/reactive_directives/form_group_directive.ts b/packages/forms/src/directives/reactive_directives/form_group_directive.ts index 6ec1773e2d..9b5ec7af0f 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_directive.ts @@ -69,7 +69,7 @@ export class FormGroupDirective extends ControlContainer implements Form, private _oldForm: FormGroup; directives: FormControlName[] = []; - @Input('formGroup') form: FormGroup = null; + @Input('formGroup') form: FormGroup = null !; @Output() ngSubmit = new EventEmitter(); constructor( @@ -167,10 +167,10 @@ export class FormGroupDirective extends ControlContainer implements Form, private _updateValidators() { const sync = composeValidators(this._validators); - this.form.validator = Validators.compose([this.form.validator, sync]); + this.form.validator = Validators.compose([this.form.validator !, sync !]); const async = composeAsyncValidators(this._asyncValidators); - this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator, async]); + this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator !, async !]); } private _checkFormPresent() { diff --git a/packages/forms/src/directives/reactive_directives/form_group_name.ts b/packages/forms/src/directives/reactive_directives/form_group_name.ts index 3dca7c5a4c..3d7904f155 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_name.ts @@ -166,7 +166,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy ngOnInit(): void { this._checkParentType(); - this.formDirective.addFormArray(this); + this.formDirective !.addFormArray(this); } ngOnDestroy(): void { @@ -175,17 +175,19 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy } } - get control(): FormArray { return this.formDirective.getFormArray(this); } + get control(): FormArray { return this.formDirective !.getFormArray(this); } - get formDirective(): FormGroupDirective { + get formDirective(): FormGroupDirective|null { return this._parent ? this._parent.formDirective : null; } get path(): string[] { return controlPath(this.name, this._parent); } - get validator(): ValidatorFn { return composeValidators(this._validators); } + get validator(): ValidatorFn|null { return composeValidators(this._validators); } - get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); } + get asyncValidator(): AsyncValidatorFn|null { + return composeAsyncValidators(this._asyncValidators); + } private _checkParentType(): void { if (_hasInvalidParent(this._parent)) { diff --git a/packages/forms/src/directives/select_control_value_accessor.ts b/packages/forms/src/directives/select_control_value_accessor.ts index daf5d03727..4c5c58dd4a 100644 --- a/packages/forms/src/directives/select_control_value_accessor.ts +++ b/packages/forms/src/directives/select_control_value_accessor.ts @@ -15,7 +15,7 @@ export const SELECT_VALUE_ACCESSOR: Provider = { multi: true }; -function _buildValueString(id: string, value: any): string { +function _buildValueString(id: string | null, value: any): string { if (id == null) return `${value}`; if (value && typeof value === 'object') value = 'Object'; return `${id}: ${value}`.slice(0, 50); @@ -118,7 +118,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor { writeValue(value: any): void { this.value = value; - const id: string = this._getOptionId(value); + const id: string|null = this._getOptionId(value); if (id == null) { this._renderer.setElementProperty(this._elementRef.nativeElement, 'selectedIndex', -1); } @@ -142,7 +142,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor { _registerOption(): string { return (this._idCounter++).toString(); } /** @internal */ - _getOptionId(value: any): string { + _getOptionId(value: any): string|null { for (const id of Array.from(this._optionMap.keys())) { if (this._compareWith(this._optionMap.get(id), value)) return id; } diff --git a/packages/forms/src/directives/select_multiple_control_value_accessor.ts b/packages/forms/src/directives/select_multiple_control_value_accessor.ts index 960cea7a64..37591b5e6f 100644 --- a/packages/forms/src/directives/select_multiple_control_value_accessor.ts +++ b/packages/forms/src/directives/select_multiple_control_value_accessor.ts @@ -149,9 +149,9 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor } /** @internal */ - _getOptionId(value: any): string { + _getOptionId(value: any): string|null { for (const id of Array.from(this._optionMap.keys())) { - if (this._compareWith(this._optionMap.get(id)._value, value)) return id; + if (this._compareWith(this._optionMap.get(id) !._value, value)) return id; } return null; } @@ -159,7 +159,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor /** @internal */ _getOptionValue(valueString: string): any { const id: string = _extractId(valueString); - return this._optionMap.has(id) ? this._optionMap.get(id)._value : valueString; + return this._optionMap.has(id) ? this._optionMap.get(id) !._value : valueString; } } diff --git a/packages/forms/src/directives/shared.ts b/packages/forms/src/directives/shared.ts index c8342bd334..8823b62818 100644 --- a/packages/forms/src/directives/shared.ts +++ b/packages/forms/src/directives/shared.ts @@ -27,55 +27,55 @@ import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './valida export function controlPath(name: string, parent: ControlContainer): string[] { - return [...parent.path, name]; + return [...parent.path !, name]; } export function setUpControl(control: FormControl, dir: NgControl): void { if (!control) _throwError(dir, 'Cannot find control with'); if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with'); - control.validator = Validators.compose([control.validator, dir.validator]); - control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]); - dir.valueAccessor.writeValue(control.value); + control.validator = Validators.compose([control.validator !, dir.validator]); + control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]); + dir.valueAccessor !.writeValue(control.value); // view -> model - dir.valueAccessor.registerOnChange((newValue: any) => { + dir.valueAccessor !.registerOnChange((newValue: any) => { dir.viewToModelUpdate(newValue); control.markAsDirty(); control.setValue(newValue, {emitModelToViewChange: false}); }); // touched - dir.valueAccessor.registerOnTouched(() => control.markAsTouched()); + dir.valueAccessor !.registerOnTouched(() => control.markAsTouched()); control.registerOnChange((newValue: any, emitModelEvent: boolean) => { // control -> view - dir.valueAccessor.writeValue(newValue); + dir.valueAccessor !.writeValue(newValue); // control -> ngModel if (emitModelEvent) dir.viewToModelUpdate(newValue); }); - if (dir.valueAccessor.setDisabledState) { + if (dir.valueAccessor !.setDisabledState) { control.registerOnDisabledChange( - (isDisabled: boolean) => { dir.valueAccessor.setDisabledState(isDisabled); }); + (isDisabled: boolean) => { dir.valueAccessor !.setDisabledState !(isDisabled); }); } // re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4 dir._rawValidators.forEach((validator: Validator | ValidatorFn) => { if ((validator).registerOnValidatorChange) - (validator).registerOnValidatorChange(() => control.updateValueAndValidity()); + (validator).registerOnValidatorChange !(() => control.updateValueAndValidity()); }); dir._rawAsyncValidators.forEach((validator: AsyncValidator | AsyncValidatorFn) => { if ((validator).registerOnValidatorChange) - (validator).registerOnValidatorChange(() => control.updateValueAndValidity()); + (validator).registerOnValidatorChange !(() => control.updateValueAndValidity()); }); } export function cleanUpControl(control: FormControl, dir: NgControl) { - dir.valueAccessor.registerOnChange(() => _noControlError(dir)); - dir.valueAccessor.registerOnTouched(() => _noControlError(dir)); + dir.valueAccessor !.registerOnChange(() => _noControlError(dir)); + dir.valueAccessor !.registerOnTouched(() => _noControlError(dir)); dir._rawValidators.forEach((validator: any) => { if (validator.registerOnValidatorChange) { @@ -105,9 +105,9 @@ function _noControlError(dir: NgControl) { function _throwError(dir: AbstractControlDirective, message: string): void { let messageEnd: string; - if (dir.path.length > 1) { - messageEnd = `path: '${dir.path.join(' -> ')}'`; - } else if (dir.path[0]) { + if (dir.path !.length > 1) { + messageEnd = `path: '${dir.path!.join(' -> ')}'`; + } else if (dir.path ![0]) { messageEnd = `name: '${dir.path}'`; } else { messageEnd = 'unspecified name attribute'; @@ -115,11 +115,12 @@ function _throwError(dir: AbstractControlDirective, message: string): void { throw new Error(`${message} ${messageEnd}`); } -export function composeValidators(validators: Array): ValidatorFn { +export function composeValidators(validators: Array): ValidatorFn|null { return validators != null ? Validators.compose(validators.map(normalizeValidator)) : null; } -export function composeAsyncValidators(validators: Array): AsyncValidatorFn { +export function composeAsyncValidators(validators: Array): AsyncValidatorFn| + null { return validators != null ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) : null; } @@ -147,12 +148,12 @@ export function isBuiltInAccessor(valueAccessor: ControlValueAccessor): boolean // TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented export function selectValueAccessor( - dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor { + dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor|null { if (!valueAccessors) return null; - let defaultAccessor: ControlValueAccessor; - let builtinAccessor: ControlValueAccessor; - let customAccessor: ControlValueAccessor; + let defaultAccessor: ControlValueAccessor|undefined = undefined; + let builtinAccessor: ControlValueAccessor|undefined = undefined; + let customAccessor: ControlValueAccessor|undefined = undefined; valueAccessors.forEach((v: ControlValueAccessor) => { if (v.constructor === DefaultValueAccessor) { defaultAccessor = v; diff --git a/packages/forms/src/form_builder.ts b/packages/forms/src/form_builder.ts index 1384ea1d38..b10797c04d 100644 --- a/packages/forms/src/form_builder.ts +++ b/packages/forms/src/form_builder.ts @@ -39,7 +39,7 @@ export class FormBuilder { * * See the {@link FormGroup} constructor for more details. */ - group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any} = null): FormGroup { + group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any}|null = null): FormGroup { const controls = this._reduceControls(controlsConfig); const validator: ValidatorFn = extra != null ? extra['validator'] : null; const asyncValidator: AsyncValidatorFn = extra != null ? extra['asyncValidator'] : null; @@ -54,8 +54,8 @@ export class FormBuilder { * */ control( - formState: Object, validator: ValidatorFn|ValidatorFn[] = null, - asyncValidator: AsyncValidatorFn|AsyncValidatorFn[] = null): FormControl { + formState: Object, validator?: ValidatorFn|ValidatorFn[]|null, + asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null): FormControl { return new FormControl(formState, validator, asyncValidator); } @@ -64,8 +64,8 @@ export class FormBuilder { * configuration, with the given optional `validator` and `asyncValidator`. */ array( - controlsConfig: any[], validator: ValidatorFn = null, - asyncValidator: AsyncValidatorFn = null): FormArray { + controlsConfig: any[], validator?: ValidatorFn|null, + asyncValidator?: AsyncValidatorFn|null): FormArray { const controls = controlsConfig.map(c => this._createControl(c)); return new FormArray(controls, validator, asyncValidator); } diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index cd00942f60..d072b7ed05 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -42,7 +42,7 @@ function _find(control: AbstractControl, path: Array| string, del } if (path instanceof Array && (path.length === 0)) return null; - return (>path).reduce((v, name) => { + return (>path).reduce((v: AbstractControl, name) => { if (v instanceof FormGroup) { return v.controls[name] || null; } @@ -55,13 +55,14 @@ function _find(control: AbstractControl, path: Array| string, del }, control); } -function coerceToValidator(validator: ValidatorFn | ValidatorFn[]): ValidatorFn { - return Array.isArray(validator) ? composeValidators(validator) : validator; +function coerceToValidator(validator?: ValidatorFn | ValidatorFn[] | null): ValidatorFn|null { + return Array.isArray(validator) ? composeValidators(validator) : validator || null; } -function coerceToAsyncValidator(asyncValidator: AsyncValidatorFn | AsyncValidatorFn[]): - AsyncValidatorFn { - return Array.isArray(asyncValidator) ? composeAsyncValidators(asyncValidator) : asyncValidator; +function coerceToAsyncValidator(asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null): + AsyncValidatorFn|null { + return Array.isArray(asyncValidator) ? composeAsyncValidators(asyncValidator) : + asyncValidator || null; } /** @@ -90,7 +91,7 @@ export abstract class AbstractControl { private _parent: FormGroup|FormArray; private _asyncValidationSubscription: any; - constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {} + constructor(public validator: ValidatorFn|null, public asyncValidator: AsyncValidatorFn|null) {} /** * The value of the control. @@ -209,7 +210,7 @@ export abstract class AbstractControl { * Sets the synchronous validators that are active on this control. Calling * this will overwrite any existing sync validators. */ - setValidators(newValidator: ValidatorFn|ValidatorFn[]): void { + setValidators(newValidator: ValidatorFn|ValidatorFn[]|null): void { this.validator = coerceToValidator(newValidator); } @@ -322,7 +323,7 @@ export abstract class AbstractControl { this._statusChanges.emit(this._status); } - this._updateAncestors(onlySelf); + this._updateAncestors(!!onlySelf); this._onDisabledChange.forEach((changeFn) => changeFn(true)); } @@ -338,7 +339,7 @@ export abstract class AbstractControl { this._forEachChild((control: AbstractControl) => { control.enable({onlySelf: true}); }); this.updateValueAndValidity({onlySelf: true, emitEvent}); - this._updateAncestors(onlySelf); + this._updateAncestors(!!onlySelf); this._onDisabledChange.forEach((changeFn) => changeFn(false)); } @@ -409,7 +410,7 @@ export abstract class AbstractControl { return this.validator ? this.validator(this) : null; } - private _runAsyncValidator(emitEvent: boolean): void { + private _runAsyncValidator(emitEvent?: boolean): void { if (this.asyncValidator) { this._status = PENDING; const obs = toObservable(this.asyncValidator(this)); @@ -465,7 +466,7 @@ export abstract class AbstractControl { * * * `this.form.get(['person', 'name']);` */ - get(path: Array|string): AbstractControl { return _find(this, path, '.'); } + get(path: Array|string): AbstractControl|null { return _find(this, path, '.'); } /** * Returns true if the control with the given path has the error specified. Otherwise @@ -473,7 +474,7 @@ export abstract class AbstractControl { * * If no path is given, it checks for the error on the present control. */ - getError(errorCode: string, path: string[] = null): any { + getError(errorCode: string, path?: string[]): any { const control = path ? this.get(path) : this; return control && control._errors ? control._errors[errorCode] : null; } @@ -484,9 +485,7 @@ export abstract class AbstractControl { * * If no path is given, it checks for the error on the present control. */ - hasError(errorCode: string, path: string[] = null): boolean { - return !!this.getError(errorCode, path); - } + hasError(errorCode: string, path?: string[]): boolean { return !!this.getError(errorCode, path); } /** * Retrieves the top-level ancestor of this control. @@ -635,8 +634,8 @@ export class FormControl extends AbstractControl { _onChange: Function[] = []; constructor( - formState: any = null, validator: ValidatorFn|ValidatorFn[] = null, - asyncValidator: AsyncValidatorFn|AsyncValidatorFn[] = null) { + formState: any = null, validator?: ValidatorFn|ValidatorFn[]|null, + asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) { super(coerceToValidator(validator), coerceToAsyncValidator(asyncValidator)); this._applyFormState(formState); this.updateValueAndValidity({onlySelf: true, emitEvent: false}); @@ -831,9 +830,9 @@ export class FormControl extends AbstractControl { */ export class FormGroup extends AbstractControl { constructor( - public controls: {[key: string]: AbstractControl}, validator: ValidatorFn = null, - asyncValidator: AsyncValidatorFn = null) { - super(validator, asyncValidator); + public controls: {[key: string]: AbstractControl}, validator?: ValidatorFn|null, + asyncValidator?: AsyncValidatorFn|null) { + super(validator || null, asyncValidator || null); this._initObservables(); this._setUpControls(); this.updateValueAndValidity({onlySelf: true, emitEvent: false}); @@ -1137,9 +1136,9 @@ export class FormGroup extends AbstractControl { */ export class FormArray extends AbstractControl { constructor( - public controls: AbstractControl[], validator: ValidatorFn = null, - asyncValidator: AsyncValidatorFn = null) { - super(validator, asyncValidator); + public controls: AbstractControl[], validator?: ValidatorFn|null, + asyncValidator?: AsyncValidatorFn|null) { + super(validator || null, asyncValidator || null); this._initObservables(); this._setUpControls(); this.updateValueAndValidity({onlySelf: true, emitEvent: false}); diff --git a/packages/forms/src/validators.ts b/packages/forms/src/validators.ts index 78cbee570f..de355fcbd4 100644 --- a/packages/forms/src/validators.ts +++ b/packages/forms/src/validators.ts @@ -143,9 +143,11 @@ export class Validators { * Compose multiple validators into a single function that returns the union * of the individual error maps. */ - static compose(validators: ValidatorFn[]): ValidatorFn { + static compose(validators: null): null; + static compose(validators: (ValidatorFn|null|undefined)[]): ValidatorFn|null; + static compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null { if (!validators) return null; - const presentValidators = validators.filter(isPresent); + const presentValidators: ValidatorFn[] = validators.filter(isPresent) as any; if (presentValidators.length == 0) return null; return function(control: AbstractControl) { @@ -153,9 +155,9 @@ export class Validators { }; } - static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn { + static composeAsync(validators: (AsyncValidatorFn|null)[]): AsyncValidatorFn|null { if (!validators) return null; - const presentValidators = validators.filter(isPresent); + const presentValidators: AsyncValidatorFn[] = validators.filter(isPresent) as any; if (presentValidators.length == 0) return null; return function(control: AbstractControl) { @@ -188,7 +190,7 @@ function _executeAsyncValidators(control: AbstractControl, validators: AsyncVali function _mergeErrors(arrayOfErrors: ValidationErrors[]): ValidationErrors|null { const res: {[key: string]: any} = arrayOfErrors.reduce((res: ValidationErrors | null, errors: ValidationErrors | null) => { - return errors != null ? {...res, ...errors} : res; + return errors != null ? {...res !, ...errors} : res !; }, {}); return Object.keys(res).length === 0 ? null : res; } diff --git a/packages/forms/test/directives_spec.ts b/packages/forms/test/directives_spec.ts index 642886c991..00001a6fff 100644 --- a/packages/forms/test/directives_spec.ts +++ b/packages/forms/test/directives_spec.ts @@ -28,7 +28,7 @@ class CustomValidatorDirective implements Validator { function asyncValidator(expected: any, timeout = 0) { return (c: AbstractControl): any => { - let resolve: (result: any) => void; + let resolve: (result: any) => void = undefined !; const promise = new Promise(res => { resolve = res; }); const res = c.value != expected ? {'async': true} : null; if (timeout == 0) { @@ -44,7 +44,7 @@ export function main() { describe('Form Directives', () => { let defaultAccessor: DefaultValueAccessor; - beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null, null, null); }); + beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null !, null !, null !); }); describe('shared', () => { describe('selectValueAccessor', () => { @@ -59,42 +59,42 @@ export function main() { () => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); }); it('should return checkbox accessor when provided', () => { - const checkboxAccessor = new CheckboxControlValueAccessor(null, null); + const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); expect(selectValueAccessor(dir, [ defaultAccessor, checkboxAccessor ])).toEqual(checkboxAccessor); }); it('should return select accessor when provided', () => { - const selectAccessor = new SelectControlValueAccessor(null, null); + const selectAccessor = new SelectControlValueAccessor(null !, null !); expect(selectValueAccessor(dir, [ defaultAccessor, selectAccessor ])).toEqual(selectAccessor); }); it('should return select multiple accessor when provided', () => { - const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null, null); + const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !); expect(selectValueAccessor(dir, [ defaultAccessor, selectMultipleAccessor ])).toEqual(selectMultipleAccessor); }); it('should throw when more than one build-in accessor is provided', () => { - const checkboxAccessor = new CheckboxControlValueAccessor(null, null); - const selectAccessor = new SelectControlValueAccessor(null, null); + const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); + const selectAccessor = new SelectControlValueAccessor(null !, null !); expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError(); }); it('should return custom accessor when provided', () => { const customAccessor = new SpyValueAccessor(); - const checkboxAccessor = new CheckboxControlValueAccessor(null, null); + const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); expect(selectValueAccessor(dir, [defaultAccessor, customAccessor, checkboxAccessor])) .toEqual(customAccessor); }); it('should return custom accessor when provided with select multiple', () => { const customAccessor = new SpyValueAccessor(); - const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null, null); + const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !); expect(selectValueAccessor( dir, [defaultAccessor, customAccessor, selectMultipleAccessor])) .toEqual(customAccessor); @@ -110,13 +110,13 @@ export function main() { it('should compose functions', () => { const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true}); const dummy2 = (_: any /** TODO #9100 */) => ({'dummy2': true}); - const v = composeValidators([dummy1, dummy2]); + const v = composeValidators([dummy1, dummy2]) !; expect(v(new FormControl(''))).toEqual({'dummy1': true, 'dummy2': true}); }); it('should compose validator directives', () => { const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true}); - const v = composeValidators([dummy1, new CustomValidatorDirective()]); + const v = composeValidators([dummy1, new CustomValidatorDirective()]) !; expect(v(new FormControl(''))).toEqual({'dummy1': true, 'custom': true}); }); }); @@ -168,7 +168,7 @@ export function main() { describe('addControl', () => { it('should throw when no control found', () => { - const dir = new FormControlName(form, null, null, [defaultAccessor]); + const dir = new FormControlName(form, null !, null !, [defaultAccessor]); dir.name = 'invalidName'; expect(() => form.addControl(dir)) @@ -176,7 +176,7 @@ export function main() { }); it('should throw for a named control when no value accessor', () => { - const dir = new FormControlName(form, null, null, null); + const dir = new FormControlName(form, null !, null !, null !); dir.name = 'login'; expect(() => form.addControl(dir)) @@ -184,8 +184,8 @@ export function main() { }); it('should throw when no value accessor with path', () => { - const group = new FormGroupName(form, null, null); - const dir = new FormControlName(group, null, null, null); + const group = new FormGroupName(form, null !, null !); + const dir = new FormControlName(group, null !, null !, null !); group.name = 'passwords'; dir.name = 'password'; @@ -315,7 +315,7 @@ export function main() { personControlGroupDir = new NgModelGroup(form, [], []); personControlGroupDir.name = 'person'; - loginControlDir = new NgModel(personControlGroupDir, null, null, [defaultAccessor]); + loginControlDir = new NgModel(personControlGroupDir, null !, null !, [defaultAccessor]); loginControlDir.name = 'login'; loginControlDir.valueAccessor = new DummyControlValueAccessor(); }); @@ -534,7 +534,7 @@ export function main() { beforeEach(() => { ngModel = new NgModel( - null, [Validators.required], [asyncValidator('expected')], [defaultAccessor]); + null !, [Validators.required], [asyncValidator('expected')], [defaultAccessor]); ngModel.valueAccessor = new DummyControlValueAccessor(); control = ngModel.control; }); @@ -566,7 +566,7 @@ export function main() { }); it('should throw when no value accessor with named control', () => { - const namedDir = new NgModel(null, null, null, null); + const namedDir = new NgModel(null !, null !, null !, null !); namedDir.name = 'one'; expect(() => namedDir.ngOnChanges({})) @@ -574,7 +574,7 @@ export function main() { }); it('should throw when no value accessor with unnamed control', () => { - const unnamedDir = new NgModel(null, null, null, null); + const unnamedDir = new NgModel(null !, null !, null !, null !); expect(() => unnamedDir.ngOnChanges({})) .toThrowError( diff --git a/packages/forms/test/form_array_spec.ts b/packages/forms/test/form_array_spec.ts index 66976e9e74..2b94b80ec7 100644 --- a/packages/forms/test/form_array_spec.ts +++ b/packages/forms/test/form_array_spec.ts @@ -15,7 +15,7 @@ import {Validators} from '../src/validators'; export function main() { function asyncValidator(expected: string, timeouts = {}) { return (c: AbstractControl) => { - let resolve: (result: any) => void; + let resolve: (result: any) => void = undefined !; const promise = new Promise(res => { resolve = res; }); const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; const res = c.value != expected ? {'async': true} : null; @@ -89,7 +89,7 @@ export function main() { new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), new FormArray([new FormControl('v4'), new FormControl('v5')]) ]); - a.at(0).get('c3').disable(); + a.at(0).get('c3') !.disable(); (a.at(1) as FormArray).at(1).disable(); expect(a.getRawValue()).toEqual([{'c2': 'v2', 'c3': 'v3'}, ['v4', 'v5']]); @@ -693,7 +693,7 @@ export function main() { describe('get', () => { it('should return null when path is null', () => { const g = new FormGroup({}); - expect(g.get(null)).toEqual(null); + expect(g.get(null !)).toEqual(null); }); it('should return null when path is empty', () => { @@ -712,23 +712,23 @@ export function main() { 'nested': new FormGroup({'two': new FormControl('222')}) }); - expect(g.get(['one']).value).toEqual('111'); - expect(g.get('one').value).toEqual('111'); - expect(g.get(['nested', 'two']).value).toEqual('222'); - expect(g.get('nested.two').value).toEqual('222'); + expect(g.get(['one']) !.value).toEqual('111'); + expect(g.get('one') !.value).toEqual('111'); + expect(g.get(['nested', 'two']) !.value).toEqual('222'); + expect(g.get('nested.two') !.value).toEqual('222'); }); it('should return an element of an array', () => { const g = new FormGroup({'array': new FormArray([new FormControl('111')])}); - expect(g.get(['array', 0]).value).toEqual('111'); + expect(g.get(['array', 0]) !.value).toEqual('111'); }); }); describe('asyncValidator', () => { it('should run the async validator', fakeAsync(() => { const c = new FormControl('value'); - const g = new FormArray([c], null, asyncValidator('expected')); + const g = new FormArray([c], null !, asyncValidator('expected')); expect(g.pending).toEqual(true); @@ -793,10 +793,10 @@ export function main() { }); expect(g.valid).toBe(false); - g.get('nested').disable(); + g.get('nested') !.disable(); expect(g.valid).toBe(true); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.valid).toBe(false); }); @@ -805,36 +805,36 @@ export function main() { {nested: new FormArray([new FormControl('one')]), two: new FormControl('two')}); expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); - g.get('nested').disable(); + g.get('nested') !.disable(); expect(g.value).toEqual({'two': 'two'}); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); }); it('should ignore disabled controls when determining dirtiness', () => { const g = new FormGroup({nested: a, two: new FormControl('two')}); - g.get(['nested', 0]).markAsDirty(); + g.get(['nested', 0]) !.markAsDirty(); expect(g.dirty).toBe(true); - g.get('nested').disable(); - expect(g.get('nested').dirty).toBe(true); + g.get('nested') !.disable(); + expect(g.get('nested') !.dirty).toBe(true); expect(g.dirty).toEqual(false); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.dirty).toEqual(true); }); it('should ignore disabled controls when determining touched state', () => { const g = new FormGroup({nested: a, two: new FormControl('two')}); - g.get(['nested', 0]).markAsTouched(); + g.get(['nested', 0]) !.markAsTouched(); expect(g.touched).toBe(true); - g.get('nested').disable(); - expect(g.get('nested').touched).toBe(true); + g.get('nested') !.disable(); + expect(g.get('nested') !.touched).toBe(true); expect(g.touched).toEqual(false); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.touched).toEqual(true); }); @@ -901,7 +901,7 @@ export function main() { }); it('should clear out async array errors when disabled', fakeAsync(() => { - const arr = new FormArray([new FormControl()], null, asyncValidator('expected')); + const arr = new FormArray([new FormControl()], null !, asyncValidator('expected')); tick(); expect(arr.errors).toEqual({'async': true}); @@ -914,7 +914,7 @@ export function main() { })); it('should re-populate async array errors when enabled from a child', fakeAsync(() => { - const arr = new FormArray([new FormControl()], null, asyncValidator('expected')); + const arr = new FormArray([new FormControl()], null !, asyncValidator('expected')); tick(); expect(arr.errors).toEqual({'async': true}); @@ -988,7 +988,7 @@ export function main() { }); it('should remove control if new control is null', () => { - a.setControl(0, null); + a.setControl(0, null !); expect(a.controls[0]).not.toBeDefined(); expect(a.value).toEqual([]); }); diff --git a/packages/forms/test/form_control_spec.ts b/packages/forms/test/form_control_spec.ts index e52c23bbac..4fa4d9f3a2 100644 --- a/packages/forms/test/form_control_spec.ts +++ b/packages/forms/test/form_control_spec.ts @@ -16,7 +16,7 @@ import {FormArray} from '../src/model'; export function main() { function asyncValidator(expected: string, timeouts = {}) { return (c: FormControl) => { - let resolve: (result: any) => void; + let resolve: (result: any) => void = undefined !; const promise = new Promise(res => { resolve = res; }); const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; const res = c.value != expected ? {'async': true} : null; @@ -49,7 +49,7 @@ export function main() { describe('boxed values', () => { it('should support valid boxed values on creation', () => { - const c = new FormControl({value: 'some val', disabled: true}, null, null); + const c = new FormControl({value: 'some val', disabled: true}, null !, null !); expect(c.disabled).toBe(true); expect(c.value).toBe('some val'); expect(c.status).toBe('DISABLED'); @@ -63,13 +63,13 @@ export function main() { }); it('should not treat objects as boxed values if they have more than two props', () => { - const c = new FormControl({value: '', disabled: true, test: 'test'}, null, null); + const c = new FormControl({value: '', disabled: true, test: 'test'}, null !, null !); expect(c.value).toEqual({value: '', disabled: true, test: 'test'}); expect(c.disabled).toBe(false); }); it('should not treat objects as boxed values if disabled is missing', () => { - const c = new FormControl({value: '', test: 'test'}, null, null); + const c = new FormControl({value: '', test: 'test'}, null !, null !); expect(c.value).toEqual({value: '', test: 'test'}); expect(c.disabled).toBe(false); }); @@ -156,7 +156,7 @@ export function main() { describe('asyncValidator', () => { it('should run validator with the initial value', fakeAsync(() => { - const c = new FormControl('value', null, asyncValidator('expected')); + const c = new FormControl('value', null !, asyncValidator('expected')); tick(); expect(c.valid).toEqual(false); @@ -164,7 +164,7 @@ export function main() { })); it('should support validators returning observables', fakeAsync(() => { - const c = new FormControl('value', null, asyncValidatorReturningObservable); + const c = new FormControl('value', null !, asyncValidatorReturningObservable); tick(); expect(c.valid).toEqual(false); @@ -172,7 +172,7 @@ export function main() { })); it('should rerun the validator when the value changes', fakeAsync(() => { - const c = new FormControl('value', null, asyncValidator('expected')); + const c = new FormControl('value', null !, asyncValidator('expected')); c.setValue('expected'); tick(); @@ -193,7 +193,7 @@ export function main() { })); it('should mark the control as pending while running the async validation', fakeAsync(() => { - const c = new FormControl('', null, asyncValidator('expected')); + const c = new FormControl('', null !, asyncValidator('expected')); expect(c.pending).toEqual(true); @@ -204,7 +204,7 @@ export function main() { it('should only use the latest async validation run', fakeAsync(() => { const c = new FormControl( - '', null, asyncValidator('expected', {'long': 200, 'expected': 100})); + '', null !, asyncValidator('expected', {'long': 200, 'expected': 100})); c.setValue('long'); c.setValue('expected'); @@ -216,14 +216,14 @@ export function main() { it('should support arrays of async validator functions if passed', fakeAsync(() => { const c = - new FormControl('value', null, [asyncValidator('expected'), otherAsyncValidator]); + new FormControl('value', null !, [asyncValidator('expected'), otherAsyncValidator]); tick(); expect(c.errors).toEqual({'async': true, 'other': true}); })); it('should add single async validator', fakeAsync(() => { - const c = new FormControl('value', null); + const c = new FormControl('value', null !); c.setAsyncValidators(asyncValidator('expected')); expect(c.asyncValidator).not.toEqual(null); @@ -235,7 +235,7 @@ export function main() { })); it('should add async validator from array', fakeAsync(() => { - const c = new FormControl('value', null); + const c = new FormControl('value', null !); c.setAsyncValidators([asyncValidator('expected')]); expect(c.asyncValidator).not.toEqual(null); @@ -634,8 +634,7 @@ export function main() { tick(); expect(log).toEqual([ - '' + - 'value: \'\'', + 'value: \'\'', 'status: \'INVALID\'', 'value: \'nonEmpty\'', 'status: \'PENDING\'', @@ -935,7 +934,7 @@ export function main() { }); it('should clear out async errors when disabled', fakeAsync(() => { - const c = new FormControl('', null, asyncValidator('expected')); + const c = new FormControl('', null !, asyncValidator('expected')); tick(); expect(c.errors).toEqual({'async': true}); diff --git a/packages/forms/test/form_group_spec.ts b/packages/forms/test/form_group_spec.ts index dd2d222d10..aa62b78c9c 100644 --- a/packages/forms/test/form_group_spec.ts +++ b/packages/forms/test/form_group_spec.ts @@ -15,7 +15,7 @@ import {AbstractControl, FormArray, FormControl, FormGroup, Validators} from '@a export function main() { function asyncValidator(expected: string, timeouts = {}) { return (c: AbstractControl) => { - let resolve: (result: any) => void; + let resolve: (result: any) => void = undefined !; const promise = new Promise(res => { resolve = res; }); const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; const res = c.value != expected ? {'async': true} : null; @@ -70,7 +70,7 @@ export function main() { 'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), 'array': new FormArray([new FormControl('v4'), new FormControl('v5')]) }); - fg.get('group').get('c3').disable(); + fg.get('group') !.get('c3') !.disable(); (fg.get('array') as FormArray).at(1).disable(); expect(fg.getRawValue()) @@ -690,7 +690,7 @@ export function main() { describe('asyncValidator', () => { it('should run the async validator', fakeAsync(() => { const c = new FormControl('value'); - const g = new FormGroup({'one': c}, null, asyncValidator('expected')); + const g = new FormGroup({'one': c}, null !, asyncValidator('expected')); expect(g.pending).toEqual(true); @@ -701,7 +701,7 @@ export function main() { })); it('should set the parent group\'s status to pending', fakeAsync(() => { - const c = new FormControl('value', null, asyncValidator('expected')); + const c = new FormControl('value', null !, asyncValidator('expected')); const g = new FormGroup({'one': c}); expect(g.pending).toEqual(true); @@ -713,13 +713,13 @@ export function main() { it('should run the parent group\'s async validator when children are pending', fakeAsync(() => { - const c = new FormControl('value', null, asyncValidator('expected')); - const g = new FormGroup({'one': c}, null, asyncValidator('expected')); + const c = new FormControl('value', null !, asyncValidator('expected')); + const g = new FormGroup({'one': c}, null !, asyncValidator('expected')); tick(1); expect(g.errors).toEqual({'async': true}); - expect(g.get('one').errors).toEqual({'async': true}); + expect(g.get('one') !.errors).toEqual({'async': true}); })); }); @@ -772,10 +772,10 @@ export function main() { }); expect(g.valid).toBe(false); - g.get('nested').disable(); + g.get('nested') !.disable(); expect(g.valid).toBe(true); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.valid).toBe(false); }); @@ -784,10 +784,10 @@ export function main() { {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); - g.get('nested').disable(); + g.get('nested') !.disable(); expect(g.value).toEqual({'two': 'two'}); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); }); @@ -795,13 +795,13 @@ export function main() { const g = new FormGroup( {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); - g.get('nested.two').disable(); + g.get('nested.two') !.disable(); expect(g.value).toEqual({nested: {one: 'one'}}); - g.get('nested').disable(); + g.get('nested') !.disable(); expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); }); @@ -809,38 +809,38 @@ export function main() { const g = new FormGroup( {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); - g.get('nested.two').disable(); + g.get('nested.two') !.disable(); expect(g.value).toEqual({nested: {one: 'one'}}); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); }); it('should ignore disabled controls when determining dirtiness', () => { const g = new FormGroup( {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); - g.get('nested.one').markAsDirty(); + g.get('nested.one') !.markAsDirty(); expect(g.dirty).toBe(true); - g.get('nested').disable(); - expect(g.get('nested').dirty).toBe(true); + g.get('nested') !.disable(); + expect(g.get('nested') !.dirty).toBe(true); expect(g.dirty).toEqual(false); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.dirty).toEqual(true); }); it('should ignore disabled controls when determining touched state', () => { const g = new FormGroup( {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); - g.get('nested.one').markAsTouched(); + g.get('nested.one') !.markAsTouched(); expect(g.touched).toBe(true); - g.get('nested').disable(); - expect(g.get('nested').touched).toBe(true); + g.get('nested') !.disable(); + expect(g.get('nested') !.touched).toBe(true); expect(g.touched).toEqual(false); - g.get('nested').enable(); + g.get('nested') !.enable(); expect(g.touched).toEqual(true); }); @@ -907,7 +907,8 @@ export function main() { }); it('should clear out async group errors when disabled', fakeAsync(() => { - const g = new FormGroup({'one': new FormControl()}, null, asyncValidator('expected')); + const g = + new FormGroup({'one': new FormControl()}, null !, asyncValidator('expected')); tick(); expect(g.errors).toEqual({'async': true}); @@ -920,7 +921,8 @@ export function main() { })); it('should re-populate async group errors when enabled from a child', fakeAsync(() => { - const g = new FormGroup({'one': new FormControl()}, null, asyncValidator('expected')); + const g = + new FormGroup({'one': new FormControl()}, null !, asyncValidator('expected')); tick(); expect(g.errors).toEqual({'async': true}); @@ -1028,7 +1030,7 @@ export function main() { }); it('should remove control if new control is null', () => { - g.setControl('one', null); + g.setControl('one', null !); expect(g.controls['one']).not.toBeDefined(); expect(g.value).toEqual({}); }); diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts index 8d5ba36e43..1dfd3c63d3 100644 --- a/packages/forms/test/reactive_integration_spec.ts +++ b/packages/forms/test/reactive_integration_spec.ts @@ -166,7 +166,7 @@ export function main() { }); fixture.componentInstance.form = form; fixture.detectChanges(); - expect(form.get('login').errors).toEqual({required: true}); + expect(form.get('login') !.errors).toEqual({required: true}); const newForm = new FormGroup({ 'login': new FormControl(''), @@ -177,7 +177,7 @@ export function main() { fixture.componentInstance.form = newForm; fixture.detectChanges(); - expect(newForm.get('login').errors).toEqual({required: true}); + expect(newForm.get('login') !.errors).toEqual({required: true}); }); it('should pick up dir validators from nested form groups', () => { @@ -188,7 +188,7 @@ export function main() { }); fixture.componentInstance.form = form; fixture.detectChanges(); - expect(form.get('signin').valid).toBe(false); + expect(form.get('signin') !.valid).toBe(false); const newForm = new FormGroup({ 'signin': @@ -197,7 +197,7 @@ export function main() { fixture.componentInstance.form = newForm; fixture.detectChanges(); - expect(form.get('signin').valid).toBe(false); + expect(form.get('signin') !.valid).toBe(false); }); it('should strip named controls that are not found', () => { @@ -373,7 +373,7 @@ export function main() { it('should throw an error if compareWith is not a function', () => { const fixture = initTest(FormControlSelectWithCompareFn); - fixture.componentInstance.compareFn = null; + fixture.componentInstance.compareFn = null !; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -412,7 +412,7 @@ export function main() { it('should throw an error when compareWith is not a function', () => { const fixture = initTest(FormControlSelectMultipleWithCompareFn); - fixture.componentInstance.compareFn = null; + fixture.componentInstance.compareFn = null !; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -623,7 +623,7 @@ export function main() { it('should emit ngSubmit event with the original submit event on submit', () => { const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); - fixture.componentInstance.event = null; + fixture.componentInstance.event = null !; fixture.detectChanges(); const formEl = fixture.debugElement.query(By.css('form')).nativeElement; @@ -739,7 +739,7 @@ export function main() { it('should work with single fields and async validators', fakeAsync(() => { const fixture = initTest(FormControlComp); - const control = new FormControl('', null, uniqLoginAsyncValidator('good')); + const control = new FormControl('', null !, uniqLoginAsyncValidator('good')); fixture.debugElement.componentInstance.control = control; fixture.detectChanges(); @@ -995,10 +995,10 @@ export function main() { fixture.detectChanges(); // view -> model - expect(form.get('food').value).toEqual('chicken'); + expect(form.get('food') !.value).toEqual('chicken'); expect(inputs[1].nativeElement.checked).toEqual(false); - form.get('food').setValue('fish'); + form.get('food') !.setValue('fish'); fixture.detectChanges(); // programmatic change -> view @@ -1039,16 +1039,16 @@ export function main() { fixture.componentInstance.form = form; fixture.detectChanges(); - form.get('food').setValue(null); + form.get('food') !.setValue(null); fixture.detectChanges(); const inputs = fixture.debugElement.queryAll(By.css('input')); expect(inputs[0].nativeElement.checked).toEqual(false); - form.get('food').setValue('chicken'); + form.get('food') !.setValue('chicken'); fixture.detectChanges(); - form.get('food').setValue(undefined); + form.get('food') !.setValue(undefined); fixture.detectChanges(); expect(inputs[0].nativeElement.checked).toEqual(false); }); @@ -1139,8 +1139,8 @@ export function main() { fixture.detectChanges(); // view -> model - expect(form.get('food').value).toEqual('chicken'); - expect(form.get('nested.food').value).toEqual('fish'); + expect(form.get('food') !.value).toEqual('chicken'); + expect(form.get('nested.food') !.value).toEqual('fish'); expect(inputs[1].nativeElement.checked).toEqual(false); expect(inputs[2].nativeElement.checked).toEqual(false); @@ -1161,7 +1161,7 @@ export function main() { expect(inputs[2].nativeElement.disabled).toEqual(false); expect(inputs[3].nativeElement.disabled).toEqual(false); - form.get('food').disable(); + form.get('food') !.disable(); expect(inputs[0].nativeElement.disabled).toEqual(true); expect(inputs[1].nativeElement.disabled).toEqual(true); expect(inputs[2].nativeElement.disabled).toEqual(false); @@ -1267,9 +1267,9 @@ export function main() { expect(form.value).toEqual({'login': 'bb'}); // custom validator - expect(form.get('login').errors).toEqual({'err': true}); + expect(form.get('login') !.errors).toEqual({'err': true}); form.setValue({login: 'expected'}); - expect(form.get('login').errors).toEqual(null); + expect(form.get('login') !.errors).toEqual(null); }); it('should support non builtin input elements that fire a change event without a \'target\' property', @@ -1295,7 +1295,7 @@ export function main() { }); fixture.detectChanges(); expect(fixture.componentInstance.form.status).toEqual('DISABLED'); - expect(fixture.componentInstance.form.get('login').status).toEqual('DISABLED'); + expect(fixture.componentInstance.form.get('login') !.status).toEqual('DISABLED'); }); it('should support custom accessors without setDisabledState - formControlDirective', @@ -1539,9 +1539,9 @@ export function main() { .toEqual(pattern.nativeElement.getAttribute('pattern')); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null; - fixture.componentInstance.maxLen = null; - fixture.componentInstance.pattern = null; + fixture.componentInstance.minLen = null !; + fixture.componentInstance.maxLen = null !; + fixture.componentInstance.pattern = null !; fixture.detectChanges(); expect(form.hasError('required', ['login'])).toEqual(false); @@ -1581,9 +1581,9 @@ export function main() { fixture.detectChanges(); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null; - fixture.componentInstance.maxLen = null; - fixture.componentInstance.pattern = null; + fixture.componentInstance.minLen = null !; + fixture.componentInstance.maxLen = null !; + fixture.componentInstance.pattern = null !; fixture.detectChanges(); expect(newForm.hasError('required', ['login'])).toEqual(false); @@ -1681,7 +1681,7 @@ export function main() { const fixture = initTest(FormControlComp); const resultArr: number[] = []; fixture.componentInstance.control = - new FormControl('', null, observableValidator(resultArr)); + new FormControl('', null !, observableValidator(resultArr)); fixture.detectChanges(); tick(100); diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts index 4ae9c7cd08..87fd77bdf8 100644 --- a/packages/forms/test/template_integration_spec.ts +++ b/packages/forms/test/template_integration_spec.ts @@ -107,9 +107,9 @@ export function main() { tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name').value).toEqual({first: 'Nancy', last: 'Drew'}); - expect(form.control.get('name.first').value).toEqual('Nancy'); - expect(form.control.get('email').value).toEqual('some email'); + expect(form.control.get('name') !.value).toEqual({first: 'Nancy', last: 'Drew'}); + expect(form.control.get('name.first') !.value).toEqual('Nancy'); + expect(form.control.get('email') !.value).toEqual('some email'); })); it('should remove controls and control groups from form control model', fakeAsync(() => { @@ -121,7 +121,7 @@ export function main() { tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('email').value).toEqual('some email'); + expect(form.control.get('email') !.value).toEqual('some email'); expect(form.value).toEqual({name: {first: 'Nancy'}, email: 'some email'}); // should remove individual control successfully @@ -132,8 +132,8 @@ export function main() { expect(form.control.get('email')).toBe(null); expect(form.value).toEqual({name: {first: 'Nancy'}}); - expect(form.control.get('name').value).toEqual({first: 'Nancy'}); - expect(form.control.get('name.first').value).toEqual('Nancy'); + expect(form.control.get('name') !.value).toEqual({first: 'Nancy'}); + expect(form.control.get('name.first') !.value).toEqual('Nancy'); // should remove form group successfully fixture.componentInstance.groupShowing = false; @@ -228,7 +228,7 @@ export function main() { it('should not create a template-driven form when ngNoForm is used', () => { const fixture = initTest(NgNoFormComp); fixture.detectChanges(); - expect(fixture.debugElement.children[0].providerTokens.length).toEqual(0); + expect(fixture.debugElement.children[0].providerTokens !.length).toEqual(0); }); it('should not add novalidate when ngNoForm is used', () => { @@ -282,7 +282,7 @@ export function main() { describe('submit and reset events', () => { it('should emit ngSubmit event with the original submit event on submit', fakeAsync(() => { const fixture = initTest(NgModelForm); - fixture.componentInstance.event = null; + fixture.componentInstance.event = null !; const form = fixture.debugElement.query(By.css('form')); dispatchEvent(form.nativeElement, 'submit'); @@ -355,11 +355,11 @@ export function main() { expect(form.valid).toEqual(true); expect(form.value).toEqual({}); - let formValidity: string; - let formValue: Object; + let formValidity: string = undefined !; + let formValue: Object = undefined !; - form.statusChanges.subscribe((status: string) => formValidity = status); - form.valueChanges.subscribe((value: string) => formValue = value); + form.statusChanges !.subscribe((status: string) => formValidity = status); + form.valueChanges !.subscribe((value: string) => formValue = value); tick(); @@ -374,8 +374,8 @@ export function main() { fixture.detectChanges(); tick(); - form.get('name').valueChanges.subscribe( - () => { expect(form.get('name').dirty).toBe(true); }); + form.get('name') !.valueChanges.subscribe( + () => { expect(form.get('name') !.dirty).toBe(true); }); const inputEl = fixture.debugElement.query(By.css('input')).nativeElement; inputEl.value = 'newValue'; @@ -396,10 +396,10 @@ export function main() { inputEl.value = 'newValue'; dispatchEvent(inputEl, 'input'); - expect(form.get('name').pristine).toBe(false); + expect(form.get('name') !.pristine).toBe(false); - form.get('name').valueChanges.subscribe( - () => { expect(form.get('name').pristine).toBe(true); }); + form.get('name') !.valueChanges.subscribe( + () => { expect(form.get('name') !.pristine).toBe(true); }); dispatchEvent(formEl, 'reset'); })); @@ -418,7 +418,7 @@ export function main() { const form = fixture.debugElement.children[0].injector.get(NgForm); expect(form.value).toEqual({name: {first: '', last: 'Drew'}, email: 'some email'}); expect(form.valid).toBe(false); - expect(form.control.get('name.first').disabled).toBe(false); + expect(form.control.get('name.first') !.disabled).toBe(false); fixture.componentInstance.isDisabled = true; fixture.detectChanges(); @@ -426,7 +426,7 @@ export function main() { expect(form.value).toEqual({name: {last: 'Drew'}, email: 'some email'}); expect(form.valid).toBe(true); - expect(form.control.get('name.first').disabled).toBe(true); + expect(form.control.get('name.first') !.disabled).toBe(true); })); it('should add disabled attribute in the UI if disable() is called programmatically', @@ -438,7 +438,7 @@ export function main() { tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - form.control.get('name.first').disable(); + form.control.get('name.first') !.disable(); fixture.detectChanges(); tick(); @@ -455,7 +455,7 @@ export function main() { fixture.detectChanges(); fixture.whenStable().then(() => { const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name').disabled).toBe(true); + expect(form.control.get('name') !.disabled).toBe(true); const customInput = fixture.debugElement.query(By.css('[name="custom"]')); expect(customInput.nativeElement.disabled).toEqual(true); @@ -477,7 +477,7 @@ export function main() { fixture.detectChanges(); tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name').disabled).toBe(true); + expect(form.control.get('name') !.disabled).toBe(true); const input = fixture.debugElement.query(By.css('input')); expect(input.nativeElement.disabled).toEqual(true); @@ -495,7 +495,7 @@ export function main() { tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - form.control.get('food').disable(); + form.control.get('food') !.disable(); tick(); const inputs = fixture.debugElement.queryAll(By.css('input')); @@ -620,7 +620,7 @@ export function main() { fixture.detectChanges(); tick(); - fixture.componentInstance.food = null; + fixture.componentInstance.food = null !; fixture.detectChanges(); tick(); @@ -632,7 +632,7 @@ export function main() { fixture.detectChanges(); tick(); - fixture.componentInstance.food = undefined; + fixture.componentInstance.food = undefined !; fixture.detectChanges(); tick(); expect(inputs[0].nativeElement.checked).toEqual(false); @@ -724,7 +724,7 @@ export function main() { const fixture = initTest(NgModelSelectWithNullForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}]; - comp.selectedCity = null; + comp.selectedCity = null !; fixture.detectChanges(); const select = fixture.debugElement.query(By.css('select')); @@ -745,7 +745,7 @@ export function main() { it('should throw an error when compareWith is not a function', () => { const fixture = initTest(NgModelSelectWithCustomCompareFnForm); const comp = fixture.componentInstance; - comp.compareFn = null; + comp.compareFn = null !; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -833,7 +833,7 @@ export function main() { it('should throw an error when compareWith is not a function', () => { const fixture = initTest(NgModelSelectMultipleWithCustomCompareFnForm); const comp = fixture.componentInstance; - comp.compareFn = null; + comp.compareFn = null !; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -885,7 +885,7 @@ export function main() { tick(); const control = - fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox'); + fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !; const input = fixture.debugElement.query(By.css('input')); expect(input.nativeElement.checked).toBe(false); @@ -921,7 +921,7 @@ export function main() { tick(); const control = - fixture.debugElement.children[0].injector.get(NgForm).control.get('email'); + fixture.debugElement.children[0].injector.get(NgForm).control.get('email') !; const input = fixture.debugElement.query(By.css('input')); expect(control.hasError('email')).toBe(false); @@ -1114,9 +1114,9 @@ export function main() { .toEqual(pattern.nativeElement.getAttribute('pattern')); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null; - fixture.componentInstance.maxLen = null; - fixture.componentInstance.pattern = null; + fixture.componentInstance.minLen = null !; + fixture.componentInstance.maxLen = null !; + fixture.componentInstance.pattern = null !; fixture.detectChanges(); expect(form.control.hasError('required', ['required'])).toEqual(false); diff --git a/packages/forms/test/validators_spec.ts b/packages/forms/test/validators_spec.ts index da1f6824dd..b44c8ce7c0 100644 --- a/packages/forms/test/validators_spec.ts +++ b/packages/forms/test/validators_spec.ts @@ -179,33 +179,33 @@ export function main() { }); it('should not error on "null" pattern', - () => expect(Validators.pattern(null)(new FormControl('aaAA'))).toBeNull()); + () => expect(Validators.pattern(null !)(new FormControl('aaAA'))).toBeNull()); it('should not error on "undefined" pattern', - () => expect(Validators.pattern(undefined)(new FormControl('aaAA'))).toBeNull()); + () => expect(Validators.pattern(undefined !)(new FormControl('aaAA'))).toBeNull()); }); describe('compose', () => { it('should return null when given null', - () => { expect(Validators.compose(null)).toBe(null); }); + () => { expect(Validators.compose(null !)).toBe(null); }); it('should collect errors from all the validators', () => { - const c = Validators.compose([validator('a', true), validator('b', true)]); + const c = Validators.compose([validator('a', true), validator('b', true)]) !; expect(c(new FormControl(''))).toEqual({'a': true, 'b': true}); }); it('should run validators left to right', () => { - const c = Validators.compose([validator('a', 1), validator('a', 2)]); + const c = Validators.compose([validator('a', 1), validator('a', 2)]) !; expect(c(new FormControl(''))).toEqual({'a': 2}); }); it('should return null when no errors', () => { - const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]); + const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]) !; expect(c(new FormControl(''))).toBeNull(); }); it('should ignore nulls', () => { - const c = Validators.compose([null, Validators.required]); + const c = Validators.compose([null !, Validators.required]) !; expect(c(new FormControl(''))).toEqual({'required': true}); }); }); @@ -221,13 +221,13 @@ export function main() { } it('should return null when given null', - () => { expect(Validators.composeAsync(null)).toBeNull(); }); + () => { expect(Validators.composeAsync(null !)).toBeNull(); }); it('should collect errors from all the validators', fakeAsync(() => { const v = Validators.composeAsync( - [promiseValidator({'one': true}), promiseValidator({'two': true})]); + [promiseValidator({'one': true}), promiseValidator({'two': true})]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); tick(); @@ -236,10 +236,10 @@ export function main() { })); it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => { - const v = Validators.composeAsync( - [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]); + const v = Validators.composeAsync([normalizeAsyncValidator( + new AsyncValidatorDirective('expected', {'one': true}))]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); tick(); @@ -248,9 +248,9 @@ export function main() { })); it('should return null when no errors', fakeAsync(() => { - const v = Validators.composeAsync([promiseValidator({'one': true})]); + const v = Validators.composeAsync([promiseValidator({'one': true})]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('expected'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); tick(); @@ -259,9 +259,9 @@ export function main() { })); it('should ignore nulls', fakeAsync(() => { - const v = Validators.composeAsync([promiseValidator({'one': true}), null]); + const v = Validators.composeAsync([promiseValidator({'one': true}), null !]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); tick(); @@ -279,13 +279,13 @@ export function main() { } it('should return null when given null', - () => { expect(Validators.composeAsync(null)).toBeNull(); }); + () => { expect(Validators.composeAsync(null !)).toBeNull(); }); it('should collect errors from all the validators', () => { const v = Validators.composeAsync( - [observableValidator({'one': true}), observableValidator({'two': true})]); + [observableValidator({'one': true}), observableValidator({'two': true})]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); @@ -294,19 +294,19 @@ export function main() { it('should normalize and evaluate async validator-directives correctly', () => { const v = Validators.composeAsync( - [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]); + [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) - .subscribe((errors: {[key: string]: any}) => errorMap = errors); + .subscribe((errors: {[key: string]: any}) => errorMap = errors) !; expect(errorMap).toEqual({'one': true}); }); it('should return null when no errors', () => { - const v = Validators.composeAsync([observableValidator({'one': true})]); + const v = Validators.composeAsync([observableValidator({'one': true})]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('expected'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); @@ -314,9 +314,9 @@ export function main() { }); it('should ignore nulls', () => { - const v = Validators.composeAsync([observableValidator({'one': true}), null]); + const v = Validators.composeAsync([observableValidator({'one': true}), null !]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); @@ -329,9 +329,9 @@ export function main() { } const v = Validators.composeAsync( - [getTimerObs(100, {one: true}), getTimerObs(200, {two: true})]); + [getTimerObs(100, {one: true}), getTimerObs(200, {two: true})]) !; - let errorMap: {[key: string]: any}; + let errorMap: {[key: string]: any} = undefined !; first.call(v(new FormControl('invalid'))) .subscribe((errors: {[key: string]: any}) => errorMap = errors); diff --git a/packages/forms/tsconfig-build.json b/packages/forms/tsconfig-build.json index 74d0fa0488..8cfa4196f1 100644 --- a/packages/forms/tsconfig-build.json +++ b/packages/forms/tsconfig-build.json @@ -4,6 +4,7 @@ "declaration": true, "stripInternal": true, "experimentalDecorators": true, + "strictNullChecks": true, "module": "es2015", "moduleResolution": "node", "outDir": "../../dist/packages/forms", diff --git a/tools/public_api_guard/forms/forms.d.ts b/tools/public_api_guard/forms/forms.d.ts index 889a1fc264..fdfc00298b 100644 --- a/tools/public_api_guard/forms/forms.d.ts +++ b/tools/public_api_guard/forms/forms.d.ts @@ -1,6 +1,6 @@ /** @stable */ export declare abstract class AbstractControl { - asyncValidator: AsyncValidatorFn; + asyncValidator: AsyncValidatorFn | null; readonly dirty: boolean; readonly disabled: boolean; readonly enabled: boolean; @@ -15,10 +15,10 @@ export declare abstract class AbstractControl { readonly touched: boolean; readonly untouched: boolean; readonly valid: boolean; - validator: ValidatorFn; + validator: ValidatorFn | null; readonly value: any; readonly valueChanges: Observable; - constructor(validator: ValidatorFn, asyncValidator: AsyncValidatorFn); + constructor(validator: ValidatorFn | null, asyncValidator: AsyncValidatorFn | null); clearAsyncValidators(): void; clearValidators(): void; disable({onlySelf, emitEvent}?: { @@ -29,7 +29,7 @@ export declare abstract class AbstractControl { onlySelf?: boolean; emitEvent?: boolean; }): void; - get(path: Array | string): AbstractControl; + get(path: Array | string): AbstractControl | null; getError(errorCode: string, path?: string[]): any; hasError(errorCode: string, path?: string[]): boolean; markAsDirty({onlySelf}?: { @@ -54,7 +54,7 @@ export declare abstract class AbstractControl { emitEvent?: boolean; }): void; setParent(parent: FormGroup | FormArray): void; - setValidators(newValidator: ValidatorFn | ValidatorFn[]): void; + setValidators(newValidator: ValidatorFn | ValidatorFn[] | null): void; abstract setValue(value: any, options?: Object): void; updateValueAndValidity({onlySelf, emitEvent}?: { onlySelf?: boolean; @@ -64,21 +64,21 @@ export declare abstract class AbstractControl { /** @stable */ export declare abstract class AbstractControlDirective { - readonly control: AbstractControl; - readonly dirty: boolean; - readonly disabled: boolean; - readonly enabled: boolean; + readonly abstract control: AbstractControl | null; + readonly dirty: boolean | null; + readonly disabled: boolean | null; + readonly enabled: boolean | null; readonly errors: ValidationErrors | null; - readonly invalid: boolean; - readonly path: string[]; - readonly pending: boolean; - readonly pristine: boolean; - readonly statusChanges: Observable; - readonly touched: boolean; - readonly untouched: boolean; - readonly valid: boolean; + readonly invalid: boolean | null; + readonly path: string[] | null; + readonly pending: boolean | null; + readonly pristine: boolean | null; + readonly statusChanges: Observable | null; + readonly touched: boolean | null; + readonly untouched: boolean | null; + readonly valid: boolean | null; readonly value: any; - readonly valueChanges: Observable; + readonly valueChanges: Observable | null; getError(errorCode: string, path?: string[]): any; hasError(errorCode: string, path?: string[]): boolean; reset(value?: any): void; @@ -86,11 +86,11 @@ export declare abstract class AbstractControlDirective { /** @stable */ export declare class AbstractFormGroupDirective extends ControlContainer implements OnInit, OnDestroy { - readonly asyncValidator: AsyncValidatorFn; + readonly asyncValidator: AsyncValidatorFn | null; readonly control: FormGroup; - readonly formDirective: Form; + readonly formDirective: Form | null; readonly path: string[]; - readonly validator: ValidatorFn; + readonly validator: ValidatorFn | null; ngOnDestroy(): void; ngOnInit(): void; } @@ -125,10 +125,10 @@ export declare class CheckboxRequiredValidator extends RequiredValidator { export declare const COMPOSITION_BUFFER_MODE: InjectionToken; /** @stable */ -export declare class ControlContainer extends AbstractControlDirective { - readonly formDirective: Form; +export declare abstract class ControlContainer extends AbstractControlDirective { + readonly formDirective: Form | null; name: string; - readonly path: string[]; + readonly path: string[] | null; } /** @stable */ @@ -175,7 +175,7 @@ export interface Form { export declare class FormArray extends AbstractControl { controls: AbstractControl[]; readonly length: number; - constructor(controls: AbstractControl[], validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn); + constructor(controls: AbstractControl[], validator?: ValidatorFn | null, asyncValidator?: AsyncValidatorFn | null); at(index: number): AbstractControl; getRawValue(): any[]; insert(index: number, control: AbstractControl): void; @@ -198,12 +198,12 @@ export declare class FormArray extends AbstractControl { /** @stable */ export declare class FormArrayName extends ControlContainer implements OnInit, OnDestroy { - readonly asyncValidator: AsyncValidatorFn; + readonly asyncValidator: AsyncValidatorFn | null; readonly control: FormArray; - readonly formDirective: FormGroupDirective; + readonly formDirective: FormGroupDirective | null; name: string; readonly path: string[]; - readonly validator: ValidatorFn; + readonly validator: ValidatorFn | null; constructor(parent: ControlContainer, validators: any[], asyncValidators: any[]); ngOnDestroy(): void; ngOnInit(): void; @@ -211,18 +211,18 @@ export declare class FormArrayName extends ControlContainer implements OnInit, O /** @stable */ export declare class FormBuilder { - array(controlsConfig: any[], validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn): FormArray; - control(formState: Object, validator?: ValidatorFn | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]): FormControl; + array(controlsConfig: any[], validator?: ValidatorFn | null, asyncValidator?: AsyncValidatorFn | null): FormArray; + control(formState: Object, validator?: ValidatorFn | ValidatorFn[] | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null): FormControl; group(controlsConfig: { [key: string]: any; }, extra?: { [key: string]: any; - }): FormGroup; + } | null): FormGroup; } /** @stable */ export declare class FormControl extends AbstractControl { - constructor(formState?: any, validator?: ValidatorFn | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]); + constructor(formState?: any, validator?: ValidatorFn | ValidatorFn[] | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null); patchValue(value: any, options?: { onlySelf?: boolean; emitEvent?: boolean; @@ -245,14 +245,14 @@ export declare class FormControl extends AbstractControl { /** @stable */ export declare class FormControlDirective extends NgControl implements OnChanges { - readonly asyncValidator: AsyncValidatorFn; + readonly asyncValidator: AsyncValidatorFn | null; readonly control: FormControl; form: FormControl; isDisabled: boolean; model: any; readonly path: string[]; update: EventEmitter<{}>; - readonly validator: ValidatorFn; + readonly validator: ValidatorFn | null; viewModel: any; constructor(validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[]); ngOnChanges(changes: SimpleChanges): void; @@ -269,7 +269,7 @@ export declare class FormControlName extends NgControl implements OnChanges, OnD name: string; readonly path: string[]; update: EventEmitter<{}>; - readonly validator: ValidatorFn; + readonly validator: ValidatorFn | null; constructor(parent: ControlContainer, validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[]); ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void; @@ -283,7 +283,7 @@ export declare class FormGroup extends AbstractControl { }; constructor(controls: { [key: string]: AbstractControl; - }, validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn); + }, validator?: ValidatorFn | null, asyncValidator?: AsyncValidatorFn | null); addControl(name: string, control: AbstractControl): void; contains(controlName: string): boolean; getRawValue(): any; @@ -371,10 +371,10 @@ export declare const NG_VALUE_ACCESSOR: InjectionToken; /** @stable */ export declare abstract class NgControl extends AbstractControlDirective { - readonly asyncValidator: AsyncValidatorFn; - name: string; - readonly validator: ValidatorFn; - valueAccessor: ControlValueAccessor; + readonly asyncValidator: AsyncValidatorFn | null; + name: string | null; + readonly validator: ValidatorFn | null; + valueAccessor: ControlValueAccessor | null; abstract viewToModelUpdate(newValue: any): void; } @@ -417,7 +417,7 @@ export declare class NgForm extends ControlContainer implements Form { /** @stable */ export declare class NgModel extends NgControl implements OnChanges, OnDestroy { - readonly asyncValidator: AsyncValidatorFn; + readonly asyncValidator: AsyncValidatorFn | null; readonly control: FormControl; readonly formDirective: any; isDisabled: boolean; @@ -429,7 +429,7 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy { }; readonly path: string[]; update: EventEmitter<{}>; - readonly validator: ValidatorFn; + readonly validator: ValidatorFn | null; viewModel: any; constructor(parent: ControlContainer, validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[]); ngOnChanges(changes: SimpleChanges): void; @@ -532,8 +532,9 @@ export interface ValidatorFn { /** @stable */ export declare class Validators { - static compose(validators: ValidatorFn[]): ValidatorFn; - static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn; + static compose(validators: null): null; + static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null; + static composeAsync(validators: (AsyncValidatorFn | null)[]): AsyncValidatorFn | null; static email(control: AbstractControl): ValidationErrors | null; static maxLength(maxLength: number): ValidatorFn; static minLength(minLength: number): ValidatorFn;