mongoose-tsgen
A Typescript interface generator for Mongoose that works out of the box.
Requires only a few lines of additional code to support, and is compatible with all Mongoose features. This CLI works by importing your schema definitions, parsing the structure and generating an index.d.ts file containing interfaces for all your schemas. A section for custom interfaces and types is provided at the bottom of index.d.ts
. This will remain untouched when re-generating the interfaces unless the --fresh
flag is provided.
Example
NOTE: The CLI supports Mongoose Models written in both Typescript and Javascript. Examples are written in Typescript.
user.ts
import mongoose, { IUser, IUserModel } 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: IUser) { return `${this.firstName} ${this.lastName}` });
UserSchema.methods = {
isMetadataString(this: IUser) {
return typeof this.metadata === "string";
}
}
UserSchema.statics = {
async getFriends(
this: IUserModel,
friendUids: IUser["_id"][]
): Promise<any[]> {
const friends = await this.aggregate([
{ $match: { _id: { $in: friendUids } } }
]);
return friends;
}
}
export const User: IUserModel = mongoose.model<IUser, IUserModel>("User", UserSchema);
export default User;
generated index.d.ts
import mongoose from "mongoose";
type ObjectId = mongoose.Types.ObjectId;
declare module "mongoose" {
interface IUserFriend extends mongoose.Types.Subdocument {
uid: IUser["_id"] | IUser;
nickname?: string;
}
export interface IUserModel extends Model<IUser> {
getFriends: Function;
}
export interface IUser extends Document {
email: string;
metadata?: any;
firstName: string;
lastName: string;
friends: Types.DocumentArray<IUserFriend>;
cityCoordinates?: Types.Array<number>;
name: any;
isMetadataString: Function;
}
}
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:
import mongoose from "mongoose";
const UserSchema = new Schema(...);
export const User = mongoose.model("User", UserSchema);
export default User;
import mongoose, { IUser, IUserModel } from "mongoose";
const UserSchema = new Schema(...);
export const User: IUserModel = mongoose.model<IUser, IUserModel>("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 { IUser } from "mongoose"
async function getUser(uid: string): IUser {
const user = await User.findById(uid);
return user;
}
async function editEmail(user: IUser, newEmail: string): IUser {
user.email = newEmail;
return await user.save();
}
Installation
$ npm install -g mongoose-tsgen
$ mtgen COMMAND
running command...
$ mtgen (-v|--version|version)
mongoose-tsgen/0.0.3 darwin-x64 node-v14.3.0
$ mtgen --help [COMMAND]
USAGE
$ mtgen RUN
...
Commands
mtgen run [PATH]
Generate an index.d.ts file containing Mongoose Schema interfaces. If no path
argument is provided, the tool will expect all models to be exported from ./src/models
by default.
USAGE
$ mtgen run [PATH]
OPTIONS
-d, --dry-run Print output rather than writing to file
-f, --fresh Fresh run, ignoring previously generated custom interfaces
-h, --help show CLI help
-o, --output=output [default: ./src/types/mongoose] Path of output index.d.ts file
See code: src/commands/run.ts
Coming Soon (most of the following features are already supported but use looser typing than likely desired):
- Methods and statics parameter types. Currently these are typed as
Function
. - Support for
Model.Create
. Currently new Model
must be used. - Support for setting subdocument properties without casting to any. When setting a subdocument array, Typescript will yell at you if you try and set them directly (ie
user.friends = [{ uid, name }]
) as it expects the array to contain additional subdocument properties. For now, this can be achieved by writing user.friends = [{ uid, name }] as any
. - A Mongoose plugin. This will remove the need to re-run the CLI when changes to your schema are made.
Would love any help with the listed features above.