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!!: 🤝

LinkedIn
GitHub