web-dev-qa-db-fra.com

Mongoose la manière TypeScript ...?

Essayer d'implémenter un modèle Mongoose dans TypeScript. Le récurage du Google n'a révélé qu'une approche hybride (combinant JS et TS). Comment procéder pour implémenter la classe User, sur mon approche plutôt naïve, sans le JS?

Vous voulez pouvoir utiliser IUserModel sans les bagages.

import {IUser} from './user.ts';
import {Document, Schema, Model} from 'mongoose';

// mixing in a couple of interfaces
interface IUserDocument extends IUser,  Document {}

// mongoose, why oh why '[String]' 
// TODO: investigate out why mongoose needs its own data types
let userSchema: Schema = new Schema({
  userName  : String,
  password  : String,
  firstName : String,
  lastName  : String,
  email     : String,
  activated : Boolean,
  roles     : [String]
});

// interface we want to code to?
export interface IUserModel extends Model<IUserDocument> {/* any custom methods here */}

// stumped here
export class User {
  constructor() {}
}
50
Tim McNamara

Voici comment je le fais:

export interface IUser extends mongoose.Document {
  name: string; 
  somethingElse?: number; 
};

export const UserSchema = new mongoose.Schema({
  name: {type:String, required: true},
  somethingElse: Number,
});

const User = mongoose.model<IUser>('User', UserSchema);
export default User;
77
Louay Alakkad

Une autre alternative si vous souhaitez détacher vos définitions de type et l'implémentation de la base de données.

import {IUser} from './user.ts';
import * as mongoose from 'mongoose';

type UserType = IUser & mongoose.Document;
const User = mongoose.model<UserType>('User', new mongoose.Schema({
    userName  : String,
    password  : String,
    /* etc */
}));

Inspiration d'ici: https://github.com/Appsilon/styleguide/wiki/mongoose-TypeScript-models

23
Gábor Imre

Désolé pour le nécropostage, mais cela peut être intéressant pour quelqu'un. Je pense que Typegoose fournit une manière plus moderne et élégante de définir des modèles

Voici un exemple tiré de la documentation:

import { prop, Typegoose, ModelType, InstanceType } from 'typegoose';
import * as mongoose from 'mongoose';

mongoose.connect('mongodb://localhost:27017/test');

class User extends Typegoose {
    @prop()
    name?: string;
}

const UserModel = new User().getModelForClass(User);

// UserModel is a regular Mongoose Model with correct types
(async () => {
    const u = new UserModel({ name: 'JohnDoe' });
    await u.save();
    const user = await UserModel.findOne();

    // prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
    console.log(user);
})();

Pour un scénario de connexion existant, vous pouvez utiliser comme suit (ce qui peut être plus probable dans les situations réelles et non couvert dans la documentation):

import { prop, Typegoose, ModelType, InstanceType } from 'typegoose';
import * as mongoose from 'mongoose';

const conn = mongoose.createConnection('mongodb://localhost:27017/test');

class User extends Typegoose {
    @prop()
    name?: string;
}

// Notice that the collection name will be 'users':
const UserModel = new User().getModelForClass(User, {existingConnection: conn});

// UserModel is a regular Mongoose Model with correct types
(async () => {
    const u = new UserModel({ name: 'JohnDoe' });
    await u.save();
    const user = await UserModel.findOne();

    // prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
    console.log(user);
})();
12
Dimanoid

Essayez ts-mongoose. Il utilise des types conditionnels pour effectuer le mappage.

import { createSchema, Type, typedModel } from 'ts-mongoose';

const UserSchema = createSchema({
  username: Type.string(),
  email: Type.string(),
});

const User = typedModel('User', UserSchema);
7
sky

Ajoutez simplement une autre manière:

import { IUser } from './user.ts';
import * as mongoose from 'mongoose';

interface IUserModel extends IUser, mongoose.Document {}

const User = mongoose.model<IUserModel>('User', new mongoose.Schema({
    userName: String,
    password: String,
    // ...
}));

Et la différence entre interface et type, veuillez lire cette réponse

De cette façon, vous pouvez ajouter des typages de méthodes statiques Mongoose:

interface IUserModel extends IUser, mongoose.Document {
  generateJwt: () => string
}
6
Hongbo Miao

Si vous avez installé @types/mongoose

npm install --save-dev @types/mongoose

Vous pouvez ainsi faire

import {IUser} from './user.ts';
import { Document, Schema, model} from 'mongoose';

type UserType = IUser & Document;
const User = model<UserType>('User', new Schema({
    userName  : String,
    password  : String,
    /* etc */
}));

PS: Copié la réponse de @Hongbo Miao

4
AdityaParab

Voici un moyen typé fort de faire correspondre un modèle simple avec un schéma de mangouste. Le compilateur s'assurera que les définitions transmises à mongoose.Schema correspondent à l'interface. Une fois que vous avez le schéma, vous pouvez utiliser

common.ts

export type IsRequired<T> =
  undefined extends T
  ? false
  : true;

export type FieldType<T> =
  T extends number ? typeof Number :
  T extends string ? typeof String :
  Object;

export type Field<T> = {
  type: FieldType<T>,
  required: IsRequired<T>,
  enum?: Array<T>
};

export type ModelDefinition<M> = {
  [P in keyof M]-?:
    M[P] extends Array<infer U> ? Array<Field<U>> :
    Field<M[P]>
};

ser.ts

import * as mongoose from 'mongoose';
import { ModelDefinition } from "./common";

interface User {
  userName  : string,
  password  : string,
  firstName : string,
  lastName  : string,
  email     : string,
  activated : boolean,
  roles     : Array<string>
}

// The typings above expect the more verbose type definitions,
// but this has the benefit of being able to match required
// and optional fields with the corresponding definition.
// TBD: There may be a way to support both types.
const definition: ModelDefinition<User> = {
  userName  : { type: String, required: true },
  password  : { type: String, required: true },
  firstName : { type: String, required: true },
  lastName  : { type: String, required: true },
  email     : { type: String, required: true },
  activated : { type: Boolean, required: true },
  roles     : [ { type: String, required: true } ]
};

const schema = new mongoose.Schema(
  definition
);

Une fois que vous avez votre schéma, vous pouvez utiliser les méthodes mentionnées dans d’autres réponses telles que

const userModel = mongoose.model<User & mongoose.Document>('User', schema);
1
bingles

Avec ça vscode intellisense fonctionne à la fois

  • Type d'utilisateur User.findOne
  • instance d'utilisateur u1._id

Le code:

// imports
import { ObjectID } from 'mongodb'
import { Document, model, Schema, SchemaDefinition } from 'mongoose'

import { authSchema, IAuthSchema } from './userAuth'

// the model

export interface IUser {
  _id: ObjectID, // !WARNING: No default value in Schema
  auth: IAuthSchema
}

// IUser will act like it is a Schema, it is more common to use this
// For example you can use this type at passport.serialize
export type IUserSchema = IUser & SchemaDefinition
// IUser will act like it is a Document
export type IUserDocument = IUser & Document

export const userSchema = new Schema<IUserSchema>({
  auth: {
    required: true,
    type: authSchema,
  }
})

export default model<IUserDocument>('user', userSchema)

0
tomitheninja