Skip to content

Make AbstractControl#value a union of T and ValidationErrors #21168

Description

@yawaramin

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

AbstractControl#value currently has type any (after #20040, it will have generic type T). AbstractControl#errors has type ValidationErrors | null. Currently we have to manually check for validation errors. It's easy to forget this check.

Expected behavior

  • Type ValidationErrors is redefined as interface ValidationErrors<T> {currentValue: T, validators: Set<string>}
  • There is a type ValueOrErrors<T> defined as T | ValidationErrors<T>
  • There is a corresponding namepace ValueOrErrors with predicate functions that test ValueOrErrors<T> values to determine their runtime subtype (T or ValidationErrors<T>)
  • There is a property AbstractControl#valueOrErrors with type ValueOrErrors<T>
  • AbstractControl#value is deprecated and later removed
  • AbstractControl#errors is deprecated and later removed
  • AbstractControl#valid is deprecated and later removed
  • AbstractControl#invalid is deprecated and later removed

This forces the developer to check for errors in order to compile their code. It also decouples the concepts of a form control having a value/error from queries on the value/error type. E.g.:

if (ValueOrErrors.isValue(inputName.valueOrErrors))
  PersonService.add(inputName.valueOrErrors);
else
  console.log(inputName.valueOrErrors.validators);

Minimal reproduction of the problem with instructions

Example copied from https://angular.io/guide/form-validation :

ngOnInit(): void {
  this.heroForm = new FormGroup({
    'name': new FormControl(this.hero.name, [
      Validators.required, Validators.minLength(4)])
  });
}

// ... type in 'Bob' in the 'name' field ...

// Returns 'Bob' which hasn't passed the minimum length validation:
get name() { return this.heroForm.get('name'); }

What is the motivation / use case for changing the behavior?

Forms gather input from the untrusted outside world, hence it makes sense to make them as safe as possible. Recruiting the TypeScript compiler to help enforce safety checks is a big benefit.

Environment


Angular version: (all)


Browser:
- [x] Chrome (desktop) version XX
- [x] Chrome (Android) version XX
- [x] Chrome (iOS) version XX
- [x] Firefox version XX
- [x] Safari (desktop) version XX
- [x] Safari (iOS) version XX
- [x] IE version XX
- [x] Edge version XX
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

Depends on: #20040

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: formsfeatureLabel used to distinguish feature request from other issues

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions