简体   繁体   中英

Mongoose + Typescript -> Exporting model interface

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!

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.

try this:

const UserSchema: Schema = new Schema(
  {
   ..
  }
);
type UserDoc = IUser & Document;

export interface UserDocument extends UserDoc {}

// For model
export interface UserModel extends Model<UserDocument> {}

export default model<UserDocument>("User", UserSchema);

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> {}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM