Angular - Argument of type 'IContact[] | undefined' is not assignable to parameter of type 'IContact[]'

Viewed 118

In Angular-12 project I have this JSON API Response:

    {
      "message": "Employee Detail.",
      "error": false,
      "code": 200,
      "results": {
        "employee": {
            "id": 8,
            "first_name": "JONAH",
            "last_name": "YAKUBU",
            "other_name": "AKWETEY",
            "employeecontacts": [
                {
                    "id": 1,
                    "phone_type_id": 2,
                    "employee_id": 8,
                    "phone_number": "014566778",
                    "is_primary_contact_number": 1,
                    "phonetypes": {
                        "id": 2,
                        "type_name": "Arena",
                    }
                },
                {
                    "id": 2,
                    "phone_type_id": 3,
                    "employee_id": 8,
                    "phone_number": "016766709",
                    "is_primary_contact_number": 0,
                    "phonetypes": {
                        "id": 2,
                        "type_name": "Office",
                    }
                }
            ]
          }
      }
    }

Interface:

    export class EmployeeResponse {
      results!: { employee: IEmployee; };
    }

    export interface IEmployee {
      id?: number;
      first_name?: string;
      other_name?: string;
      last_name?: string;
      employeecontacts?: IContact[];
    }

    export interface IContact {
      id?: number;
      phone_number: string;
      phone_type_id?: number;
      phonetypes?: {id:number,type_name:string};
      is_primary_contact_number?: boolean;
    }

service:

    getEmployeeById(id: number): Observable<EmployeeResponse> {
      return this.http.get<EmployeeResponse>(this.api.baseURL + 'employees/fetchbyid/' + id, this.httpOptions);
    }

    public updateEmployee(id: number, employee: IEmployee): Observable<any> {
      return this.http.put(this.api.baseURL + 'employees/update/' + id, employee, this.httpOptions);
    }

component:

_id!: number;
contactInfoForm!: FormGroup;
contactdata!: IEmployee;

ngOnInit(): void {
  this.isLoading = true;
  this._id = this.route.snapshot.params['id'];
  this.updateContact();
  this.loadContactById();
}

loadContactById() {
  this.employeeService
    .getEmployeeById(this._id)
    .subscribe((data: EmployeeResponse) => {
      this.contactdata = data.results.employee;
      this.contactInfoForm.patchValue({
        first_name: this.contactdata.first_name,

      });
    });
  this.contactInfoForm.setControl('contacts', this.SetExistingContacts(this.contactdata.employeecontacts));
}

SetExistingContacts(contactSets: IContact[]): FormArray {
  const formarray = new FormArray([]);
  contactSets.forEach(c => {
    formarray.push(this.fb.group({
      phone_number: c.phone_number,
      phone_type_id: c.phone_type_id,
      is_primary_contact_number: c.is_primary_contact_number
    }));
  });
  return formarray;
}

updateContact() {
  this.contactInfoForm = this.fb.group({
    id: [''],
    first_name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(50)]],
    contacts: this.fb.array([
      this.addContactFormGroup()
    ])
  });
}

addContactFormGroup(): FormGroup {
  return this.fb.group({
    phone_type_id: ['', Validators.required],
    phone_number: ['', [Validators.required, Validators.maxLength(15)]],
    is_primary_contact_number: ['']
  });
}

public addContactButtonClick() {
  const contacts = this.contactInfoForm.get('contacts') as FormArray
  contacts.push(this.addContactFormGroup())
}

get contacts() {
  return this.contactInfoForm.controls['contacts'] as FormArray;
}

getContactFormGroup(index: number): FormGroup {
  return this.contacts.at(index) as FormGroup;
}

I am tying to load data into the Dynamic FormArray and then update it.

I got this error from the component:

Argument of type 'IContact[] | undefined' is not assignable to parameter of type 'IContact[]'.

Type 'undefined' is not assignable to type 'IContact[]'.ts(2345)

And this line is highlighted:

(this.contactdata.employeecontacts));

in:

this.contactInfoForm.setControl('contacts', this.SetExistingContacts(this.contactdata.employeecontacts));

How do I resolve the error and then update the data?

Thanks

3 Answers

Your IEmployee interface has an optional property:

employeecontacts?: IContact[];

So it's expected to have that error message. Typescript doesn't know if it's going to be undefined or an actual value.

Try the following:

 this.contactInfoForm.setControl(
      'contacts', 
      this.SetExistingContacts(this.contactdata.employeecontacts!) //note the ! at the end
 );

Also, I noticed the line of code where the error is, it should be inside the subscribe function. You are storing data into this.contactdata and you trying to get employeecontacts outside, it doesn't have any data yet.

loadContactById() {
  this.employeeService
    .getEmployeeById(this._id)
    .subscribe((data: EmployeeResponse) => {
      this.contactdata = data.results.employee;
      this.contactInfoForm.patchValue({
        first_name: this.contactdata.first_name,
      });
     
     this.contactInfoForm.setControl(
        'contacts', 
        this.SetExistingContacts(this.contactdata.employeecontacts)
     );
    });
 }

You are likely working under strict-null-checks.

From https://www.typescriptlang.org/docs/handbook/2/objects.html

in this example, both xPos and yPos are considered optional. We can choose to provide either of them, so every call above to paintShape is valid. All optionality really says is that if the property is set, it better have a specific type.

We can also read from those properties - but when we do under strictNullChecks, TypeScript will tell us they’re potentially undefined.

So your property employeecontacts?: IContact[] is treated as employeecontacts: IContact[] | undefined under the hood.

This is where strict-null-checks's assignment rules come in.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html

Under strict null checks, null and undefined are only assignable to themselves and any. In the first list of examples provided by the above link, your case would be the equivalent to the line x = y; // Error, just with your IContact[] type instead of number;

There's a couple solutions:

  1. Turn off strict-null-checks, which will allow you to assign null and undefined to any type.

  2. Cast your your call, i.e. this.SetExistingContacts(this.contactdata.employeecontacts as IContact[] | undefined)); or this.SetExistingContacts(this.contactdata.employeecontacts!));

  3. Change the parameter type for SetExistingContacts to IContact[] | undefined

As extension of @Andres answer, you can use Logical OR (||) operator to check this.contactdata.employeecontacts have the value.

When it has no value (null, undefined), it will assign [] (empty array) for the argument of SetExistingContacts method.

this.contactInfoForm.setControl(
  'contacts',
  this.SetExistingContacts(this.contactdata.employeecontacts || [])
);

Meanwhile, you need to move the above lines in the subscribe event, otherwise this.contactdata.employeecontacts will not have value from the response.

loadContactById() {
  this.employeeService
    .getEmployeeById(this._id)
    .subscribe((data: EmployeeResponse) => {
      this.contactdata = data.results.employee;
      this.contactInfoForm.patchValue({
        first_name: this.contactdata.first_name
      });

      this.contactInfoForm.setControl(
        'contacts',
        this.SetExistingContacts(this.contactdata.employeecontacts || [])
      );
    });
}

Sample Solution on StackBlitz

Related