Suppose I have multiple classes with a similar method for saving models. For example, many services that include a saveModel method:
public async saveModel(newModel: IModel): Promise<IModel> {
return await newModel.save();
}
I created a generic method like this:
public async saveModel<P extends Document>(newModel: P): Promise<P> {
return await newModel.save();
}
All the services (using this method) extend a class containing the generic method. Now, any service can call
answerService.saveModel(newAnswer)
and save the answer. However, things got complicated when I realized I could pass an object of type IQuestion or IDinosaur as long as it extends the Mongoose Document interface.
Is there a way to enforce specific interfaces for each service? How can I ensure that only objects of type IAnswer are saved by the answerService? Does the answerService need to implement an interface with the saveModel signature like this:
interface IAnswerService {
saveModel(newModel: IAnswer): Promise<IAnswer>;
}
This is an overview of how everything looks:
EntityService.ts
import { Document } from 'mongoose';
export class EntityService implements IEntityService {
public async saveModel<P extends Document>(newModel: P): Promise<P> {
try {
const savedModel = await newModel.save();
return savedModel;
} catch (err) {
console.log(err);
return null;
}
}
}
answer.service.ts
import { Answer, IAnswer } from '@models/answer.model';
import { EntityService } from '@classes/EntityService';
class AnswerService extends EntityService {
private static instance: AnswerService;
private constructor() {
super();
}
static getInstance(): AnswerService {
if (!AnswerService.instance) {
AnswerService.instance = new AnswerService();
}
return AnswerService.instance;
}
}
const answerService = AnswerService.getInstance();
export default answerService;
answer.model.ts
import mongoose, { Schema, Document, Model } from 'mongoose';
export interface IAnswer extends Document {
answerText: string;
posterID: string;
}
const AnswerSchema: Schema = new Schema({
answerText: {type: String, required: true},
posterID: {type: String, required: true}
}, {
minimize: false
});
export const Answer: Model<IAnswer> = mongoose.model<IAnswer>('Answer', AnswerSchema);
All other interfaces inherit from the same elements as IAnswer (Model, Schema, Document).
The usage of the AnswerService typically looks like this:
import answerService from 'answer.service';
import { Answer } from 'answer.model';
const answer = new Answer({
answerText: 'Stuff',
posterID: '123456789'
})
answerService.saveModel(answer);