Мангуст, машинописный путь ...?

39

Попытка реализовать модель Mongoose в Typescript. Очистка Google выявила только гибридный подход (сочетающий JS и TS). Как можно было бы реализовать класс User, по моему довольно наивному подходу, без JS?

Хотите, чтобы IUserModel без багажа.

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() {}
}
  • 0
    User не может быть классом, потому что его создание - асинхронная операция. Он должен вернуть обещание, поэтому вы должны вызвать User.create({...}).then...
  • 0
    В частности, учитывая код в ОП, не могли бы вы пояснить, почему User не может быть классом?
Показать ещё 1 комментарий
Теги:
mongoose

7 ответов

61
Лучший ответ

Вот как я это делаю:

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;
  • 1
    извините, но как определяется «мангуст» в TS?
  • 4
    import * as mongoose from 'mongoose'; или import mongoose = require('mongoose');
Показать ещё 15 комментариев
20

Другая альтернатива, если вы хотите отделить свои определения типов и реализацию базы данных.

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 */
}));

Вдохновение отсюда: https://github.com/Appsilon/styleguide/wiki/mongoose-typescript-models

7

Извините за некропост, но кому-то это может быть интересно. Я думаю, что Typegoose предоставляет более современный и элегантный способ определения моделей

Вот пример из документации:

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);
})();

Для существующего сценария подключения вы можете использовать следующее (что может быть более вероятным в реальных ситуациях и раскрыто в документации):

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);
})();
  • 0
    Я также пришел к такому выводу, но беспокоюсь, что typegoose не имеет достаточной поддержки ... проверяя их статистику npm, это всего лишь 3 тыс. Еженедельных загрузок, и, таким образом, существует почти 100 открытых проблем Github, большинство из которых не имеют комментариев и некоторые из которых выглядят так, как будто они должны были быть закрыты давным-давно
5

Просто добавьте другой способ:

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,
    // ...
}));

И разница между interface и type, пожалуйста, прочитайте этот ответ

Этот способ имеет преимущество, вы можете добавить типичные статические методы Mongoose:

interface IUserModel extends IUser, mongoose.Document {
  generateJwt: () => string
}
  • 0
    где вы определили generateJwt ?
  • 0
    @rels const User = mongoose.model.... password: String, generateJwt: () => { return someJwt; } })); по сути, generateJwt становится еще одним свойством модели.
Показать ещё 2 комментария
4

Если вы установили @types/mongoose

npm install --save-dev @types/mongoose

Вы можете так

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: Скопировано @Hongbo Miao ответ

2

Попробуйте ts-mongoose. Он использует условные типы для отображения.

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

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

const User = typedModel('User', UserSchema);
  • 0
    Выглядит очень перспективно! Спасибо, что поделился! :)
  • 0
    Вот это да. Это замки очень гладкие. Будем рады попробовать!
0

Здесь строгий типизированный способ сопоставления простой модели со схемой мангуста. Компилятор гарантирует, что определения, переданные в mongoose.Schema, соответствуют интерфейсу. Как только у вас есть схема, вы можете использовать

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]>
};

user.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
);

Если у вас есть схема, вы можете использовать методы, упомянутые в других ответах, таких как

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню