I want to export only my model's interfaces instead of the Document so that nobody can modify my model if it's not inside it's own class methods. I have defined the interface and the schema like this:
IUser:
interface IUser {
_id: string;
name: string;
email: string;
created_at: number;
updated_at: number;
last_login: number;
}
And the Schema:
let userSchema: Mongoose.Schema = new Mongoose.Schema({
'name': String,
'email': String,
'created_at': {'type': Date, 'default': Date.now},
'updated_at': {'type': Date, 'default': Date.now},
'last_login': {'type': Number, 'default': 0},
});
interface UserDocument extends IUser, Mongoose.Document {}
And then the model
// Model
let Users: Mongoose.Model<UserDocument> = Mongoose.model<UserDocument>('User', userSchema);
So i just export the IUser and a class User that basically has all the methods to update my model.
The problem is that typescript complains if i add the _id to my interface, but i actually need it, otherwise i will need to pass the UserDocument and that's what i didn't wanted to do. The error typescript gives me is:
error TS2320: Interface 'UserDocument' cannot simultaneously extend types 'IUser' and 'Document'. Named property '_id' of types 'IUser' and 'Document' are not identical.
Any ideas how i can add the _id property to my interface?
Thanks!
Mongoose introduced officially supported TypeScript bindings in v5. 11.0. Mongoose's index.
A Mongoose model is a wrapper on the Mongoose schema. A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.
Typegoose aims to solve this problem by defining only a TypeScript interface (class), which needs to be enhanced with special Typegoose decorators (like @prop ). Under the hood it uses the Reflect & reflect-metadata API to retrieve the types of the properties, so redundancy can be significantly reduced.
mongoose. model() returns a Model ( It is a constructor, compiled from Schema definitions).
Try:
interface UserDocument extends IUser, Mongoose.Document {
_id: string;
}
It will resolve the conflict between IUser._id (string) vs Mongoose.Document._id (any).
Update:
As pointed out in comments, currently it gives a incompatible override for member from "Document"
, so another workaround must be used. Intersection types is a solution that can be used. That said, the following can be done:
type UserDocument = IUser & Mongoose.Document;
Alternatively, if you do not want UserDocument
anymore:
// Model
let Users = Mongoose.model<IUser & Mongoose.Document>('User', userSchema);
It is worth noting that there is a side effect in this solution. The conflicting properties will have the types intersected, so IUser._id (string) & Mongoose.Document._id (any)
results in UserDocument._id (any)
, for example.
I just had this exact issue, where I wanted to keep the User interface properties as separate from Mongoose as possible. I managed to solve the problem using the Omit utility type.
Here is your original code using that type:
import { Document, Model, ObjectId } from 'mongoose';
export interface IUser {
_id: ObjectId;
name: string;
email: string;
created_at: number;
updated_at: number;
last_login: number;
}
export interface IUserDocument extends Omit<IUser, '_id'>, Document {}
export interface IUserModel extends Model<IUserDocument> {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With