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
"?
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.
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 {
// ...
}