0

In an Angular application, I am using a base interface with some common properties and two child interfaces extending it:

interface BaseConfig {
   id: number;
   name: string;
   type: ConfigType 
}

interface UserConfig extends BaseConfig  {
   socialNr: number;
}

interface OrderConfig extends BaseConfig  {
   shipDate: Date;
}

enum ConfigType {
   User: 'user',
   Order: 'order'
}

The goal is to leverage polymorphism and use the base class as type for both child interfaces, for example:

export interface State extends EntityState<BaseConfig> {
  loaded: boolean;
  selectedBookConfig: BaseConfig | null;  // This can be either UserConfig or OrderConfig
} 

On the component side I can check the type and cast accordingly:

 if (this.config?.type == ConfigType.User) {
     this.view = (this.config as UserConfig).socialNr;
 }

The problem is however in the templates, where I get errors for the child properties, missing in the base interface. In the following snippet, I would refer to the case of an UserConfig:

// config has the base type: `BaseConfig`

<div [ngSwitch]="config.type">
    <span [ngSwitch]="ConfigType.User"> {{ config.socialNr }} </span>
</div>

The error is: Property 'socialNr' does not exist on type 'BaseConfig'.

How could I use a base interface as root/generic type for its extending interfaces/types without incurring in the template issues as above?

6
  • UserConfig and OrderConfig doesn't extendsBaseConfig, is that normal ? Commented Aug 31, 2021 at 8:28
  • 1
    They do, I forgot it in the snippet, thanks Commented Aug 31, 2021 at 8:30
  • Ok, no problem :). And are you sure it's UserConfig the type of the object ? else, with a cast like myObj as UserConfig ? Commented Aug 31, 2021 at 8:34
  • The cast works on the component side, but it does not in the template, Commented Aug 31, 2021 at 8:39
  • 1
    Yes, that would work, even though the solution is not very performant when I have several properties (the case above is an extremely simplified version of my DTOs). I will keep the question still open for now in case of other suggestions. Commented Aug 31, 2021 at 8:48

2 Answers 2

2

There is two ways.

  1. Convert into object

Such as explained in comments, if you have few types you can do like that:

In your template :

{{ getUserConfig.socialNr }}

In your component :

public get getUserConfig(): UserConfig {
   return config as UserConfig;
}
  1. Get value from obj

If you have lot of type (such as in your case), you can use this:

{{ config['socialNr'] }}

Both works fine (in my case, tested with angular)

Sign up to request clarification or add additional context in comments.

Comments

2

You should have State be a generic type as well, e.g.:

export interface State<T extends BaseConfig> extends EntityState<T> {
  loaded: boolean;
  selectedBookConfig: T | null;  // This can be either UserConfig or OrderConfig
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.