mongoose-tsgen
An plug-n-play Typescript interface generator for Mongoose.
Features
Compatibility
Installation
$ npm install -D mongoose-tsgen
$ npx mtgen --help # print usage
Usage
mtgen [ROOT_PATH]
Generate an index.d.ts file containing Mongoose Schema interfaces.
USAGE
$ mtgen [ROOT_PATH - default = "."]
OPTIONS
-d, --dry-run print output rather than writing to file
-h, --help show CLI help
-j, --js search for Mongoose schemas in Javascript files rather than in Typescript files
-o, --output=output [default: ./src/types/mongoose] path of output index.d.ts file
-p, --project=project [default: ./] path of tsconfig.json or its root folder
--no-format disable formatting generated files with prettier and fixing with eslint
--no-func-types disable using TS compiler API for method, static and query typings
All sub-directories of ROOT_PATH
will be searched for models/*.ts
files (or models/*.js
). Files in this folder (other than an index file) are expected to export a Mongoose model.
NOTE: --output requires a folder path or a file path ending in index.d.ts
. If the path does not exist, it will be created.
See code: src/index.ts
Example
./src/models/user.ts
import mongoose, { UserDocument, UserModel, UserQueries } from "mongoose";
const { Schema } = mongoose;
const UserSchema = new Schema({
email: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
metadata: Schema.Types.Mixed,
friends: [
{
uid: {
type: Schema.Types.ObjectId,
ref: "User",
required: true
},
nickname: String
}
],
city: {
coordinates: {
type: [Number],
index: "2dsphere"
}
}
});
UserSchema.virtual("name").get(function (this: UserDocument) {
return `${this.firstName} ${this.lastName}`;
});
UserSchema.methods = {
isMetadataString(this: UserDocument) {
return typeof this.metadata === "string";
}
};
UserSchema.statics = {
async getFriends(this: UserModel, friendUids: UserDocument["_id"][]) {
return await this.aggregate([{ $match: { _id: { $in: friendUids } } }]);
}
};
const queryFuncs: UserQueries = {
populateFriends() {
return this.populate("friends.uid", "firstName lastName");
}
};
UserSchema.query = queryFuncs;
export const User: UserModel = mongoose.model<UserDocument, UserModel>("User", UserSchema);
export default User;
generate interfaces
$ mtgen
generated interface file ./src/types/mongoose/index.d.ts
import mongoose from "mongoose";
declare module "mongoose" {
interface UserFriend {
uid: User["_id"] | User;
nickname?: string;
_id: mongoose.Types.ObjectId;
}
interface UserQueries {
populateFriends<Q extends mongoose.DocumentQuery<any, UserDocument, {}>>(
this: Q,
...args: any[]
): Q;
}
interface UserModel extends Model<UserDocument, UserQueries> {
getFriends: (this: any, friendUids: UserDocument["_id"][]) => Promise<any>;
}
interface User {
email: string;
firstName: string;
lastName: string;
friends: UserFriend[];
city: {
coordinates?: number[];
};
_id: mongoose.Types.ObjectId;
}
type UserFriendDocument = mongoose.Types.Subdocument & {
uid: UserDocument["_id"] | UserDocument;
} & UserFriend;
type UserDocument = mongoose.Document & {
metadata?: any;
friends: mongoose.Types.DocumentArray<UserFriendDocument>;
city: {};
name: any;
isMetadataString: (this: any) => boolean;
} & User;
}
Initializing Schemas
Once you've generated your index.d.ts file, all you need to do is add the following types to your schema definitions:
user.ts before:
import mongoose from "mongoose";
const UserSchema = new Schema(...);
export const User = mongoose.model("User", UserSchema);
export default User;
user.ts after:
import mongoose, { UserDocument, UserModel } from "mongoose";
const UserSchema = new Schema(...);
export const User: UserModel = mongoose.model<UserDocument, UserModel>("User", UserSchema);
export default User;
Then you can import the interfaces across your application from the Mongoose module and use them for document types:
import { UserDocument } from "mongoose";
async function getUser(uid: string): UserDocument {
const user = await User.findById(uid);
return user;
}
async function editEmail(user: UserDocument, newEmail: string): UserDocument {
user.email = newEmail;
return await user.save();
}
Development