Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Exclude entire interface based on inner property

I’m developing a frontend application and our backend generates an SDK that the frontend can use for typings/service calls/etc. One of the main objects that the frontend uses is causing me a few headaches in regards to proper typings. Here are very simplified example types generated by the SDK…

export enum ModelTypes {
  ModelOne = 'ModelOne',
  ModelTwo = 'ModelTwo'
}

export interface ModelOne {
  score: ModelOneScores;
  metadata: ModelOneMetadata;
  modelOneProp_A: string;
  modelOneProp_B: string;
}

export interface ModelTwo {
  modelTwoProp_A: number;
  modelTwoProp_B: number;
}

export interface Model {
  id: string;
  created: Date;
  updated: Date;
  data: (ModelOne | ModelTwo);
  type: ModelTypes;
}

As you can see, ModelOne has the "score" and "metadata" properties, while ModelTwo does not. I have a function to retrieve a score from any "Model" like so (you can assume that inside ModelOneScores or any other ModelNScores interface is an "averageScore" property):

export function getModelScore(model: Sdk.Model): number {
  switch (model.type) {
    case Sdk.ModelTypes.ModelOne:
      return model.data.score.averageScore;
  }
}

Obviously, TypeScript complains because score doesn’t exist on ModelTwo. What is a type I can use to tell TypeScript "I will never pass a ModelTwo to this function so score will always be available on Sdk.Model"?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Note: there are more Models than just ModelTwo that don’t have score, so preferably a catch-all type to exclude any models without the score property would be a nice-to-have.

>Solution :

You could define a type for these Model instances that will always have ModelOne for data instead of ModelTwo:

export type ModelWithModelOne = Exclude<Model, "data"> & {data: ModelOne};

The Exclude there isn’t strictly necessary (I kind of like it for clarity of intent), you could just do:

export type ModelWithModelOne = Exclude<Model, "data"> & {data: ModelOne};

Either way, then you’d use that in the function parameter.

Playground link

If this is completely a one-off, you could just do that with the parameter’s type directly (here I’ve left out Exclude):

export function getModelScore(model: Sdk.Model & {data: ModelOne}): number {
    // ...
}

Playground link

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading