I have a model factory that handles both basic and relational models using discriminated types. Currently, I'm using as unknown as type assertions which I'd like to avoid. Here's a simplified version:
interface BaseModelOptions {
resourceId: string;
}
interface RelationModelOptions extends BaseModelOptions {
_relations: Readonly<Record<string, ModelOptions>>;
}
type ModelOptions = BaseModelOptions | RelationModelOptions;
type RelationsType<T> = T extends RelationModelOptions
? { _relations: T['_relations'] }
: {};
type Model<TModelOptions extends ModelOptions> = Readonly<
{
resourceId: TModelOptions['resourceId'];
} & RelationsType<TModelOptions>
>;
const createModel = <TModelOptions extends ModelOptions>(
options: TModelOptions,
): Model<TModelOptions> => {
if ('_relations' in options) {
const opt = options as RelationModelOptions; // Want to avoid this assertion
return {
resourceId: opt.resourceId,
_relations: opt._relations,
} as unknown as Model<TModelOptions>; // Want to avoid this assertion
}
return {
resourceId: options.resourceId,
} as unknown as Model<TModelOptions>; // Want to avoid this assertion
};
const bojan = createModel({
resourceId: 'bojan',
_relations: {
someKey: createModel({
resourceId: 'Woop',
}),
},
});
Is there a way to structure this code to maintain the same type safety but avoid the type assertions? The factory should infer the correct return type based on whether the input has _relations or not.