【问题标题】:Mongoose the Typescript way...?猫鼬打字稿方式...?
【发布时间】:2016-04-01 15:08:21
【问题描述】:

尝试在 Typescript 中实现 Mongoose 模型。搜了一下谷歌,只发现了一种混合方式(结合JS和TS)。如果没有 JS,如何以我相当幼稚的方法实现 User 类?

希望能够在没有包袱的情况下进行 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() {}
}

【问题讨论】:

  • User 不能是一个类,因为创建一个类是异步操作。它必须返回一个承诺,所以你必须打电话给User.create({...}).then...
  • 具体来说,在OP中的代码中给出,请您详细说明为什么User不能是一个类?
  • @Erich 他们说 typeorm 不能很好地与 MongoDB 配合使用,也许 Type goose 是一个不错的选择

标签: javascript node.js mongoose typescript


【解决方案1】:

Mongoose 在 v5.11.0 中引入了官方支持的 TypeScript 绑定。 https://mongoosejs.com/docs/typescript.html 描述了 Mongoose 推荐的在 TypeScript 中使用 Mongoose 的方法。

【讨论】:

    【解决方案2】:

    嗯,我发现下面的链接真的很有帮助,作者在没有使用任何库的情况下详细描述了每一步。

    Typescript With MongoDB and Node/Express

    这真的对我很有帮助,希望对那些在不安装任何额外插件的情况下寻找解决方案的人非常有帮助。

    不过,如果您愿意,可以尝试TypeORMTypeGoose

    但我更喜欢不安装任何库:-)。

    【讨论】:

      【解决方案3】:

      官方文档不鼓励TS接口扩展Document。

      这种方法有效,但我们建议您的文档界面不要扩展 Document。使用 extends Document 使得 Mongoose 难以推断查询过滤器、精简文档和其他情况中存在哪些属性。

      TS 接口

      export interface IPerson {
        firstName: string;
        lastName: string;
        fullName: string;
      }
      

      架构

          const personSchema = new Schema<IPerson>({
            //You get intellisense of properties so less error prone
            firstName:{type:String},
            lastName:{type:String}
          })
      
         personSchema.virtual('fullName').get(function(this:IPerson) {
          return this.firstName + " " this.lastName
         });
      
         export const User = model<IPerson>('person',personSchema)
      

      【讨论】:

        【解决方案4】:

        对于为现有 Mongoose 项目寻找解决方案的任何人:

        我们最近构建了mongoose-tsgen 来解决这个问题(希望得到一些反馈!)。像 typegoose 这样的现有解决方案需要重写我们的整个模式并引入了各种不兼容性。 mongoose-tsgen 是一个简单的 CLI 工具,它生成一个 index.d.ts 文件,其中包含所有 Mongoose 模式的 Typescript 接口;它几乎不需要任何配置,并且可以与任何 Typescript 项目非常顺利地集成。

        【讨论】:

          【解决方案5】:

          根据 mongoose 文档,您必须手动定义 TS 类型。 mongoose

          但是当使用以下包时,不再需要定义 ts 接口。 mongoose-auto-ts

          换句话说,pacakge 会自动推断文档类型。

          例子:

          // Schema and model must be imported from mongoose-auto-ts.
          //import { Schema, model, connect } from 'mongoose';
          import { connect } from 'mongoose';
          import { Schema, model} from 'mongoose-auto-ts';
          
          // 1. Create an interface representing a document in MongoDB.
          // You don't need to create this interface:
          /*interface User {
            name: string;
            email: string;
            avatar?: string;
          }*/
          
          // 2. Create a Schema corresponding to the document interface.
          //No need to inject TS interface anymore
          const schema = new Schema/*<User>*/({...});
          
          // 3. Create a Model.
          //No need to inject TS interface anymore
          const UserModel = model/*<User>*/('User', schema);
          .
          .
          .
          

          【讨论】:

          • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
          【解决方案6】:

          TypeORM 是一个更好的现代解决方案。它同时支持 JavaScriptTypeScript

          TypeORM 是一种 ORM,可以在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、NativeScript、Expo 和 Electron 平台上运行,并且可以与 TypeScript 和 JavaScript(ES5、ES6、ES7、ES8)一起使用。

          它有很多features

          它的目标是始终支持最新的 JavaScript 功能并提供额外的功能来帮助您开发使用数据库的任何类型的应用程序 - 从具有几个表的小型应用程序到具有多个数据库的大型企业应用程序。

          它支持大多数数据库,如mysqlmariadbpostgrescockroachdbsqlitemssqloracle 等以及mongodb

          TypeORM 支持 Active Record 和 Data Mapper 模式,不像 当前存在的所有其他 JavaScript ORM,这意味着您可以 编写高质量、松耦合、可扩展、可维护 应用最高效的方式。

          因此无需为不同的数据库学习不同的 ORM 或框架。

          【讨论】:

            【解决方案7】:

            不确定这是您要查找的内容 但是有一个包叫Typegoose

            【讨论】:

              【解决方案8】:

              我发现以下方法最简单、最有效,因为它使用您定义的额外接口验证架构中的键,帮助您保持一切同步。

              当您在模式上添加/更改模式验证器属性(如 maxlength、小写等)时,您还会获得惊人的打字稿自动完成建议。

              双赢!

              
              import { Document, model, Schema, SchemaDefinitionProperty } from "mongoose";
              
              type TDocument<Fields> = Fields & Document;
              type TSchema<Fields> = Record<keyof Fields, SchemaDefinitionProperty>;
              
              type UserFields = {
                email: string;
                firstName?: string;
                roles?: string[];
              };
              
              const userSchema: TSchema<UserFields> = {
                email: { type: Schema.Types.String, required: true, index: true },
                firstName: { type: Schema.Types.String, maxlength: 30, trim: true },
                roles: [
                  { type: Schema.Types.String, maxlength: 20, lowercase: true },
                ],
              };
              
              export const User = model<TDocument<UserFields>>(
                "User",
                new Schema(userSchema, { timestamps: true })
              );
              
              

              最好的部分!您可以为所有模型重用 TDocument 和 TSchema 类型。

              【讨论】:

                【解决方案9】:

                最新的 mongoose 包带有 typescript 支持。您不再需要使用@types/mongoose。在此处查看我的示例。

                https://jasonching2005.medium.com/complete-guide-for-using-typescript-in-mongoose-with-lean-function-e55adf1189dc

                【讨论】:

                • 什么时候必须付费才能看到。
                • 以隐身模式打开。呵呵
                【解决方案10】:

                如果您想确保您的架构满足模型类型,反之亦然,此解决方案提供比@bingles 建议的更好的类型:

                常用类型文件: ToSchema.ts(别慌!复制粘贴就好)

                import { Document, Schema, SchemaType, SchemaTypeOpts } from 'mongoose';
                
                type NonOptionalKeys<T> = { [k in keyof T]-?: undefined extends T[k] ? never : k }[keyof T];
                type OptionalKeys<T> = Exclude<keyof T, NonOptionalKeys<T>>;
                type NoDocument<T> = Exclude<T, keyof Document>;
                type ForceNotRequired = Omit<SchemaTypeOpts<any>, 'required'> & { required?: false };
                type ForceRequired = Omit<SchemaTypeOpts<any>, 'required'> & { required: SchemaTypeOpts<any>['required'] };
                
                export type ToSchema<T> = Record<NoDocument<NonOptionalKeys<T>>, ForceRequired | Schema | SchemaType> &
                   Record<NoDocument<OptionalKeys<T>>, ForceNotRequired | Schema | SchemaType>;
                
                

                和一个示例模型:

                import { Document, model, Schema } from 'mongoose';
                import { ToSchema } from './ToSchema';
                
                export interface IUser extends Document {
                   name?: string;
                   surname?: string;
                   email: string;
                   birthDate?: Date;
                   lastLogin?: Date;
                }
                
                const userSchemaDefinition: ToSchema<IUser> = {
                   surname: String,
                   lastLogin: Date,
                   role: String, // Error, 'role' does not exist
                   name: { type: String, required: true, unique: true }, // Error, name is optional! remove 'required'
                   email: String, // Error, property 'required' is missing
                   // email: {type: String, required: true}, // correct ?
                   // Error, 'birthDate' is not defined
                };
                
                const userSchema = new Schema(userSchemaDefinition);
                
                export const User = model<IUser>('User', userSchema);
                
                
                

                【讨论】:

                • 嗯,很有希望,但没有类型检查!如果电子邮件在界面中定义为email: string,但在架构中定义为email: Number,则不会显示任何错误=(
                【解决方案11】:

                只需另加一种方式(@types/mongoose必须与npm install --save-dev @types/mongoose一起安装)

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

                还有interfacetype的区别,请看this answer

                这种方式有一个优势,可以添加Mongoose静态方法类型:

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

                【讨论】:

                • 你在哪里定义generateJwt
                • @rels const User = mongoose.model.... password: String, generateJwt: () =&gt; { return someJwt; } })); 基本上,generateJwt 成为模型的另一个属性。
                • 您会以这种方式将其作为方法添加还是将其连接到方法属性?
                • 这应该是公认的答案,因为它分离了用户定义和用户 DAL。如果您想从 mongo 切换到另一个 db 提供程序,则无需更改用户界面。
                • @RafaeldelRio:问题是关于在 TypeScript 中使用猫鼬。切换到另一个数据库与此目标背道而驰。将架构定义与IUser 接口声明分开在不同文件中 的问题在于,随着项目复杂性和开发人员数量的增加,risk of fields getting out of sync 相当高。跨度>
                【解决方案12】:

                这里的大多数答案都重复 TypeScript 类/接口和猫鼬模式中的字段。没有单一的事实来源代表着维护风险,因为项目变得更加复杂并且有更多的开发人员致力于它:字段更有可能不同步。当类与猫鼬模式位于不同的文件中时,这尤其糟糕。

                为了保持字段同步,定义一次是有意义的。有几个库可以做到这一点:

                我还没有完全相信他们中的任何一个,但 typegoose 似乎得到了积极的维护,并且开发人员接受了我的 PR。

                提前思考:当您将 GraphQL 模式添加到混合中时,会出现另一层模型复制。解决此问题的一种方法可能是从 GraphQL 架构中发送 generate TypeScript and mongoose code

                【讨论】:

                • 使用 Graphql 生成打字稿类型如果实体有额外的 Graphql 字段在 Mongoose 模式中不存在,则会出现问题,因为该类型不仅有 mongoose 模式字段,还有来自graphql.
                【解决方案13】:

                我是 Plumier 的粉丝,它有 mongoose helper但它可以独立使用,无需 Plumier 本身。与 Typegoose 不同的是,它使用 Plumier 的专用反射库采取了不同的方式,这使得使用很酷的东西成为可能。

                特点

                1. 纯 POJO(域不需要继承任何类,也不需要使用任何特殊数据类型),模型创建自动推断为T &amp; Document,因此可以访问文档相关属性。
                2. 支持TypeScript参数属性,有strict:truetsconfig配置就好了。并且使用参数属性不需要对所有属性都使用装饰器。
                3. 支持的字段属性,例如 Typegoose
                4. 配置与 mongoose 相同,因此您将很容易熟悉它。
                5. 支持继承,使编程更加自然。
                6. 模型分析,显示模型名称及其相应的集合名称、应用的配置等。

                用法

                import model, {collection} from "@plumier/mongoose"
                
                
                @collection({ timestamps: true, toJson: { virtuals: true } })
                class Domain {
                    constructor(
                        public createdAt?: Date,
                        public updatedAt?: Date,
                        @collection.property({ default: false })
                        public deleted?: boolean
                    ) { }
                }
                
                @collection()
                class User extends Domain {
                    constructor(
                        @collection.property({ unique: true })
                        public email: string,
                        public password: string,
                        public firstName: string,
                        public lastName: string,
                        public dateOfBirth: string,
                        public gender: string
                    ) { super() }
                }
                
                // create mongoose model (can be called multiple time)
                const UserModel = model(User)
                const user = await UserModel.findById()
                

                【讨论】:

                  【解决方案14】:

                  这是一个基于 @types/mongoose 包的自述文件的示例。

                  除了上面已经包含的元素之外,它还显示了如何包含常规和静态方法:

                  import { Document, model, Model, Schema } from "mongoose";
                  
                  interface IUserDocument extends Document {
                    name: string;
                    method1: () => string;
                  }
                  interface IUserModel extends Model<IUserDocument> {
                    static1: () => string;
                  }
                  
                  var UserSchema = new Schema<IUserDocument & IUserModel>({
                    name: String
                  });
                  
                  UserSchema.methods.method1 = function() {
                    return this.name;
                  };
                  UserSchema.statics.static1 = function() {
                    return "";
                  };
                  
                  var UserModel: IUserModel = model<IUserDocument, IUserModel>(
                    "User",
                    UserSchema
                  );
                  UserModel.static1(); // static methods are available
                  
                  var user = new UserModel({ name: "Success" });
                  user.method1();
                  

                  总的来说,这个 README 似乎是使用 mongoose 处理类型的绝佳资源。

                  【讨论】:

                  • 这种方法将每个字段的定义从 IUserDocument 复制到 UserSchema,随着模型变得越来越复杂,这会产生维护风险。像 ts-mongoosetypegoose 这样的包试图解决这个问题,尽管有很多语法问题。
                  【解决方案15】:

                  以下是微软员工的做法。 here

                  import mongoose from "mongoose";
                  
                  export type UserDocument = mongoose.Document & {
                      email: string;
                      password: string;
                      passwordResetToken: string;
                      passwordResetExpires: Date;
                  ...
                  };
                  
                  const userSchema = new mongoose.Schema({
                      email: { type: String, unique: true },
                      password: String,
                      passwordResetToken: String,
                      passwordResetExpires: Date,
                  ...
                  }, { timestamps: true });
                  
                  export const User = mongoose.model<UserDocument>("User", userSchema);
                  

                  我建议您在将 TypeScript 添加到 Node 项目时检查这个出色的入门项目。

                  https://github.com/microsoft/TypeScript-Node-Starter

                  【讨论】:

                  • 这会复制 mongoose 和 TypeScript 之间的每个字段,随着模型变得更加复杂,这会带来维护风险。像 ts-mongoosetypegoose 这样的解决方案解决了这个问题,尽管有很多语法问题。
                  【解决方案16】:

                  这是来自 Mongoose 文档的示例,Creating from ES6 Classes Using loadClass(),已转换为 TypeScript:

                  import { Document, Schema, Model, model } from 'mongoose';
                  import * as assert from 'assert';
                  
                  const schema = new Schema<IPerson>({ firstName: String, lastName: String });
                  
                  export interface IPerson extends Document {
                    firstName: string;
                    lastName: string;
                    fullName: string;
                  }
                  
                  class PersonClass extends Model {
                    firstName!: string;
                    lastName!: string;
                  
                    // `fullName` becomes a virtual
                    get fullName() {
                      return `${this.firstName} ${this.lastName}`;
                    }
                  
                    set fullName(v) {
                      const firstSpace = v.indexOf(' ');
                      this.firstName = v.split(' ')[0];
                      this.lastName = firstSpace === -1 ? '' : v.substr(firstSpace + 1);
                    }
                  
                    // `getFullName()` becomes a document method
                    getFullName() {
                      return `${this.firstName} ${this.lastName}`;
                    }
                  
                    // `findByFullName()` becomes a static
                    static findByFullName(name: string) {
                      const firstSpace = name.indexOf(' ');
                      const firstName = name.split(' ')[0];
                      const lastName = firstSpace === -1 ? '' : name.substr(firstSpace + 1);
                      return this.findOne({ firstName, lastName });
                    }
                  }
                  
                  schema.loadClass(PersonClass);
                  const Person = model<IPerson>('Person', schema);
                  
                  (async () => {
                    let doc = await Person.create({ firstName: 'Jon', lastName: 'Snow' });
                    assert.equal(doc.fullName, 'Jon Snow');
                    doc.fullName = 'Jon Stark';
                    assert.equal(doc.firstName, 'Jon');
                    assert.equal(doc.lastName, 'Stark');
                  
                    doc = (<any>Person).findByFullName('Jon Snow');
                    assert.equal(doc.fullName, 'Jon Snow');
                  })();
                  

                  对于静态的findByFullName方法,我不知道如何获取Person的类型信息,所以在我想调用它的时候我不得不强制转换&lt;any&gt;Person。如果您知道如何解决此问题,请添加评论。

                  【讨论】:

                  • other answers 一样,这种方法复制了接口和模式之间的字段。这可以通过拥有单一的事实来源来避免,例如通过使用ts-mongoosetypegoose。在定义 GraphQL 模式时,情况会进一步重复。
                  • 用这种方法定义 refs 的任何方法?
                  【解决方案17】:

                  有了这个 vscode intellisense 可以同时工作

                  • 用户类型 User.findOne
                  • 用户实例 u1._id

                  代码:

                  // 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)
                  
                  

                  【讨论】:

                    【解决方案18】:

                    很抱歉发布了 necropost,但这对某些人来说仍然很有趣。 我认为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);
                    })();
                    

                    【讨论】:

                    • 我也得出了这个结论,但我担心typegoose 没有足够的支持...查看他们的 npm 统计数据,每周只有 3k 的下载量,并且 rn 有近 100 个打开的 Github问题,大部分都没有cmets,还有一些看起来早就应该关闭了
                    • @Corbfon 你试过了吗?如果是这样,你的发现是什么?如果没有,是否还有其他因素让您决定不使用它?我通常看到有些人担心完全支持,但实际使用它的人似乎对它很满意
                    • @N4ppeL 我不会选择typegoose - 我们最终手动处理我们的打字,类似于this post,看起来ts-mongoose 可能有一些承诺(如后面的回答中所建议的) )
                    • 永远不要为“necroposting”道歉。 [正如你现在所知道的......] 甚至还有一个徽章(尽管 it is named Necromancer ;^D)可以做到这一点!鼓励发布新的信息和想法!
                    • @ruffin:我也真的不明白反对发布新的和最新的问题解决方案的耻辱。
                    【解决方案19】:

                    这是一种将普通模型与猫鼬模式匹配的强类型方法。编译器将确保传递给 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);
                    

                    【讨论】:

                    • 这是唯一正确的答案。其他答案都没有真正确保模式和类型/接口之间的类型兼容性。
                    • @DanDascalescu 我认为您不了解类型的工作原理。
                    • 这个答案被低估了。需要更多的赞成票。正如@JamieS 已经指出的那样,这是确保文档接口和模式之间类型兼容性的最佳解决方案。对于我的用例,我必须将 default?: any 添加到 Field 类型并将 required 设为可选。
                    • @Valkay 感谢您提醒我这颗宝石。你是对的 - 这个答案被严重低估了。
                    【解决方案20】:

                    试试ts-mongoose。它使用条件类型进行映射。

                    import { createSchema, Type, typedModel } from 'ts-mongoose';
                    
                    const UserSchema = createSchema({
                      username: Type.string(),
                      email: Type.string(),
                    });
                    
                    const User = typedModel('User', UserSchema);
                    

                    【讨论】:

                    • 披露:ts-mongoose 似乎是天造地设的。似乎是最巧妙的解决方案。
                    • 看起来不错,但我们仍然需要 mongoose 来处理 api 中的其他逻辑。
                    【解决方案21】:

                    我是这样做的:

                    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;
                    

                    【讨论】:

                    • 抱歉,TS 中的 'mongoose' 是如何定义的?
                    • import * as mongoose from 'mongoose';import mongoose = require('mongoose');
                    • 类似这样的东西:import User from '~/models/user'; User.find(/*...*/).then(/*...*/);
                    • 最后一行(导出默认 const User...)对我不起作用。我需要按照stackoverflow.com/questions/35821614/… 中的建议拆分线路
                    • 我可以在 IDE 或编译时执行 let newUser = new User({ iAmNotHere: true }) 而不会出现任何错误。那么创建界面的原因是什么?
                    【解决方案22】:

                    如果你想分离你的类型定义和数据库实现,另一种选择。

                    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

                    【讨论】:

                    • 这里的mongoose.Schema 定义是否与IUser 中的字段重复?鉴于IUser 是在不同的文件中定义的,随着项目的复杂性和开发人员数量的增加,risk that the fields with get out of sync 相当高。
                    • 是的,这是一个值得考虑的有效论点。不过,使用组件集成测试可能有助于降低风险。请注意,无论是通过 ORM(如您所建议的那样)还是手动(如在此答案中)完成,类型声明和 DB 实现都是分开的方法和架构。没有灵丹妙药……
                    • 一个项目符号可能是来自 GraphQL 定义的 generate code,用于 TypeScript 和 mongoose。
                    猜你喜欢
                    • 2016-08-28
                    • 2016-02-05
                    • 2020-05-11
                    • 1970-01-01
                    • 2017-04-11
                    • 2020-09-11
                    • 2017-01-20
                    • 2021-01-05
                    • 2018-04-27
                    相关资源
                    最近更新 更多