[SOLVED] Angular – Element implicitly has an 'any' type because expression of type 'string' can't be used to index type

Issue

I have this code in Angular-13:

export class ShowValidationErrorsComponent {

 private static readonly errorMessages = {
    'required': () => 'This field is required.',
    'minlength': (params: { requiredLength: any; }) => `Value must be at least' ${params.requiredLength} characters long.`,
    'maxlength': (params: { requiredLength: any; }) => `Value must be shorter than ${params.requiredLength} characters.`,
    'pattern': (params: { requiredPattern: string; }) => 'The required pattern is: ' + params.requiredPattern
 };

 @Input() private control!: AbstractControlDirective | AbstractControl;
 @Input() private shouldShow: boolean = true;
 @Input() extraCssClasses: string = '';

   showList(): boolean {
    return !!(this.control &&
      this.control.errors &&
      this.shouldShow);
  }

  listOfErrors(): string[] {
    let errors: string[] = [];
    for (let field of Object.keys(this.control.errors!)) {
      if (field in ShowValidationErrorsComponent.errorMessages) {
        errors.push(ShowValidationErrorsComponent.errorMessages[field](this.control.errors![field]));
      }
      else if ('message' in this.control.errors![field]) {
        errors.push(this.control.errors![field].message);
      }
    }
    return errors;
  }
}

which I used for validation. But I got this error:

Element implicitly has an ‘any’ type because expression of type
‘string’ can’t be used to index type ‘{ required: () => string;
minlength: (params: { requiredLength: any; }) => string; maxlength:
(params: { requiredLength: any; }) => string; pattern: (params: {
requiredPattern: string; }) => string; }’. No index signature with a
parameter of type ‘string’ was found on type ‘{ required: () =>
string; min

This line of code is highlighted:

ShowValidationErrorsComponent.errorMessages[field]

in

errors.push(ShowValidationErrorsComponent.errorMessagesfield);

What do I do to get it resolve?

Thanks

Solution

This happens because the implicit type of errorMessages is the following:

type ErrorMessagesType = {
    required: () => string;
    minlength: (params: { requiredLength: any }) => string;
    maxlength: (params: { requiredLength: any }) => string;
    pattern: (params: { requiredPattern: string }) => string;
}

As you can see, this is very specific to the point that it exactly matches the structure of your errorMessages value. This is because the compiler cannot make a safe guess to what your intention with that type was. So instead, it constructs the type based on what the value actually contains.

Usually, this works very well, allowing you to use the type with strong type safety everywhere. But it breaks quickly when you want to access things dynamically.

In your case, you check that the field is a valid key of that errorMessages object using field in ShowValidationErrorsComponent.errorMessages. With this, the compiler knows that doing errorMessages[field] itself is safe to access. But it has no idea which of the values you access so it cannot guess the actual type of the result.

In the end, for situations like this, you should consider specifying an explicit type that matches your intention. In this case, I would assume that you want some kind of mapping from a field name to a validation function. You can use the Record<K, T> type for this:

private static readonly errorMessages: Record<string, Function> = {
    'required': () => 'This field is required.',
    'minlength': (params: { requiredLength: any; }) => `Value must be at least' ${params.requiredLength} characters long.`,
    'maxlength': (params: { requiredLength: any; }) => `Value must be shorter than ${params.requiredLength} characters.`,
    'pattern': (params: { requiredPattern: string; }) => 'The required pattern is: ' + params.requiredPattern
};

Unfortunately, your validation functions do not share a common shape, so you cannot get a much better type there than just knowing that it’s a function. So that’s what you can use there.

With this, the compiler will at least be able to properly resolve the errorMessages[field] access and tell that the result is a function that it can call.

Answered By – poke

Answer Checked By – Dawn Plyler (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *