When working with Mongoose and TypeScript, two helper types make your life much easier:
/**
* Extracts the “plain” shape of your schema—
* just the fields you defined, without Mongoose’s built-in methods or `_id`.
*/
export type User = InferSchemaType<typeof userSchema>;
/**
* Represents a fully “hydrated” Mongoose document:
* your fields plus all of Mongoose’s methods and metadata
* (e.g. `_id`, `save()`, `populate()`, etc.).
*/
export type UserDocument = HydratedDocument<User>;
export const userModel = model<UserDocument>("user", userSchema);
InferSchemaType
• Produces a pure TypeScript type from your schema definition.
• Use it whenever you need just the data shape (e.g. DTOs, service inputs/outputs).
HydratedDocument
• Wraps your base type T with Mongoose’s document helpers.
• Use it for any function that deals with real, database-backed
documents (e.g. returns from find, create, save).
For example, in a repository interface you might write:
export interface IUserRepository {
findOneByEmail(email: string): Promise<UserDocument>;
findById(id: Types.ObjectId): Promise<UserDocument>;
create(
createUserDto: Pick<CreateUserDto, 'email' | 'password'>,
): Promise<UserDocument>;
}
Here, each method clearly promises a “live” Mongoose document (with built-in methods) while elsewhere you can rely on User for pure data shapes—keeping your boundaries and types crystal clear.
Let’s connect!!: 🤝