mongoose-tsgen
A Typescript interface generator for Mongoose Schemas that works out of the box.
Requires only a few lines of additional code to support, and is compatible with all Mongoose features. This tool works by importing your schema definitions, parsing the structure and generating an index.d.ts file containing interfaces for all your schemas. A section at the bottom of index.d.ts is provided for user-defined custom interfaces. This will remain untouched when re-generating interfaces unless the --fresh
flag is provided.
Installation
$ npm install -g mongoose-tsgen
$ mtgen # print usage
Usage
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 ./dist/models/index.js
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
Example
NOTE: Currently the CLI requires Typescript to be transpiled to Javascript. Please ensure to provide a Javascript file as the path
argument.
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"][]) {
return await this.aggregate([ { $match: { _id: { $in: friendUids } } } ]);
}
}
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:
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, { 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();
}
Coming Soon
NOTE: most of the following features are already supported but use looser typing than likely desired.
- Support running without first transpiling Typescript (run directly on Typescript files using ts-node)
- 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.