Handle objects with properties that can be of multiple types in Angular

Advertisements

I’m trying to build a search screen that uses tabs to switch between different input methods but I’m running into a Typescript error because my base object allows multiple types whereas my custom input component doesn’t.

In the parent component I have an array of objects of type Tab to define all the tabs

export interface Tab {
  id: string,
  name: string,
  desc: string,
  type: string,
  isCurrent: boolean,
  value: string | number | Checkbox[] | IDate
}

I iterate over this array, and produce different HTML based on the tab type

However, because value can be multiple different data types I get errors when I try to produce checkboxes, because I need to iterate over an array. e.g. *ngFor produces the error because tab.value might not be an array, it might be a number etc.

      <div class="govuk-form-group" *ngIf="tab.type === 'checkbox'">
        <fieldset class="govuk-fieldset" aria-describedby="{tab.id + 'hint'}">
          <div class="govuk-checkboxes" data-module="govuk-checkboxes">
            <div class="govuk-checkboxes__item" *ngFor="let checkbox of tab.value">
              <input class="govuk-checkboxes__input" id="{{checkbox.id}}" name="{{checkbox.id}}" type="checkbox" [(ngModel)]="checkbox.selected">
              <label class="govuk-label govuk-checkboxes__label" for="{{checkbox.id}}">
                {{checkbox.name}}
              </label>
            </div>
          </div>
        </fieldset>
      </div>

I have tried to use a child component, whereby Tab.value is redefined in the child as being of type Checkbox[] only and passing the tab object from parent to child. But then I get an error saying that one is not assignable to the other, which makes perfect sense because value can hold different types in both Interfaces.

Is there any way I can satisfy the Type checking, without resorting to using any?

>Solution :

Here is a way you could handle that while keeping your html exactly as is and just changing your types.
It’s a bit verbose but it lets TypeScript infer the type of the value property from the value of the type property

export type Tab = StringTab | NumberTab | DateTab | CheckboxTab;

interface TabBase {
    id: string,
    name: string,
    desc: string,
    isCurrent: boolean
}

interface StringTab extends TabBase {
    type: "string",
    value: string
}

interface NumberTab extends TabBase {
    type: "number",
    value: number
}

interface DateTab extends TabBase {
    type: "date",
    value: IDate
}

interface CheckboxTab extends TabBase {
    type: "checkbox",
    value: Checkbox[]
}

Leave a ReplyCancel reply