Javascript 猫鼬打字稿的方式......?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34482136/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mongoose the Typescript way...?
提问by Tim McNamara
Trying to implement a Mongoose model in Typescript. Scouring the Google has revealed only a hybrid approach (combining JS and TS). How would one go about implementing the User class, on my rather naive approach, without the JS?
尝试在 Typescript 中实现 Mongoose 模型。搜索 Google 只发现了一种混合方法(结合 JS 和 TS)。在没有 JS 的情况下,如何以我相当天真的方法实现 User 类?
Want to be able to IUserModel without the baggage.
想要能够没有包袱的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() {}
}
回答by Louay Alakkad
Here's how I do it:
这是我的方法:
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;
回答by Gábor Imre
Another alternative if you want to detach your type definitions and the database implementation.
如果您想分离您的类型定义和数据库实现,另一种选择。
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 from here: https://github.com/Appsilon/styleguide/wiki/mongoose-typescript-models
灵感来自这里:https: //github.com/Appsilon/styleguide/wiki/mongoose-typescript-models
回答by Dimanoid
Sorry for necroposting but this can be still interesting for someone. I think Typegooseprovides more modern and elegant way to define models
很抱歉发布尸检,但这对某些人来说仍然很有趣。我认为Typegoose提供了更现代和优雅的方式来定义模型
Here is an example from the docs:
这是文档中的一个示例:
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);
})();
For an existing connection scenario, you can use as the following (which may be more likely in the real situations and uncovered in the docs):
对于现有的连接场景,您可以使用如下(在实际情况中更有可能并且在文档中未发现):
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);
})();
回答by sky
Try ts-mongoose
. It uses conditional types to do the mapping.
试试ts-mongoose
。它使用条件类型来进行映射。
import { createSchema, Type, typedModel } from 'ts-mongoose';
const UserSchema = createSchema({
username: Type.string(),
email: Type.string(),
});
const User = typedModel('User', UserSchema);
回答by Hongbo Miao
Just add another way (@types/mongoose
must be installed with npm install --save-dev @types/mongoose
)
只需添加另一种方式(@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,
// ...
}));
And the difference between interface
and type
, please read this answer
之间的差异interface
,并type
请阅读此答案
This way has a advantage, you can add Mongoose static method typings:
这种方式有一个优势,您可以添加 Mongoose 静态方法类型:
interface IUserModel extends IUser, mongoose.Document {
generateJwt: () => string
}
回答by Dan Dascalescu
Most answers here repeat the fields in the TypeScript class/interface, and in the mongoose schema. Not having a single source of truth represents a maintenance risk, as the project becomes more complex and more developers work on it: fields are more likely to get out of sync. This is particularly bad when the class is in a different file vs. the mongoose schema.
这里的大多数答案都重复了 TypeScript 类/接口和猫鼬模式中的字段。没有单一的真实来源意味着维护风险,因为项目变得更加复杂,并且有更多的开发人员在做它:字段更有可能不同步。当类与猫鼬模式位于不同的文件中时,这尤其糟糕。
To keep fields in sync, it makes sense to define them once. There are a few libraries that do this:
为了保持字段同步,定义它们一次是有意义的。有几个库可以做到这一点:
- typeodm.io- full test coverage, good examples, no traction yet
- mongoose-decorators-ts- best English, no traction yet
- typegoose- most popular, documentation needs improvement
- ts-mongoose- doesn't use decorators, second most popular, not actively maintained
- mongoose-schema-decorators- no traction yet
- mongoose-typescript- fork of typegoose
- typeodm.io- 完整的测试覆盖,很好的例子,还没有吸引力
- mongoose-decorators-ts- 最好的英语,还没有吸引力
- typegoose- 最受欢迎,文档需要改进
- ts-mongoose- 不使用装饰器,第二流行,没有积极维护
- mongoose-schema-decorators- 还没有吸引力
- mongoose -typescript - typegoose 的分支
I haven't yet been fully convinced by any of them but typegoose seems actively maintained, and the developer accepted my PRs.
我还没有完全相信他们中的任何一个,但 typegoose 似乎得到了积极维护,开发人员接受了我的 PR。
To think one step ahead: when you add a GraphQL schema into the mix, another layer of model duplication appears. One way to overcome this problem might be to generate TypeScript and mongoose codefrom the GraphQL schema.
提前考虑一步:当您将 GraphQL 模式添加到组合中时,会出现另一层模型复制。克服这个问题的一种方法可能是从 GraphQL 模式生成 TypeScript 和mongoose代码。
回答by bingles
Here's a strong typed way to match a plain model with a mongoose schema. The compiler will ensure the definitions passed to mongoose.Schema matches the interface. Once you have the schema, you can use
这是一种将普通模型与猫鼬模式相匹配的强类型方法。编译器将确保传递给 mongoose.Schema 的定义与接口匹配。一旦你有了架构,你就可以使用
common.ts
常见的.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
用户.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
);
Once you have your schema, you can use methods mentioned in other answers such as
拥有架构后,您可以使用其他答案中提到的方法,例如
const userModel = mongoose.model<User & mongoose.Document>('User', schema);
回答by tomitheninja
With this vscode intellisenseworks on both
有了这vscode intellisense两个作品
- User TypeUser.findOne
- user instanceu1._id
- 用户类型User.findOne
- 用户实例u1._id
The 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)
回答by Lead Developer
Here's how guys at Microsoft do it. 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);
I recommend to check this excellent starter project out when you add TypeScript to your Node project.
我建议您在将 TypeScript 添加到 Node 项目时查看这个优秀的入门项目。
回答by bonjorno
I am a fans of Plumier, it has mongoose helper, but it can be used standalone without Plumier itself. Unlike Typegoose its took different path by using Plumier's dedicated reflection library, that make it possible to use cools stuff.
我是 Plumier 的粉丝,它有 mongoose helper,但它可以在没有 Plumier 本身的情况下独立使用。与 Typegoose 不同的是,它通过使用 Plumier 的专用反射库采取了不同的路径,这使得使用很酷的东西成为可能。
Features
特征
- Pure POJO (domain doesn't need to inherit from any class, nor using any special data type), Model created automatically inferred as
T & Document
thus its possible to access document related properties. - Supported TypeScript parameter properties, it's good when you have
strict:true
tsconfig configuration. And with parameter properties doesn't require decorator on all properties. - Supported field properties like Typegoose
- Configuration is the same as mongoose so you will get easily familiar with it.
- Supported inheritance that's make the programming more natural.
- Model analysis, showing model names and its appropriate collection name, configuration applied etc.
- 纯POJO(域不需要从任何类继承,也不需要使用任何特殊的数据类型),自动创建的模型推断
T & Document
它可以访问文档相关的属性。 - 支持TypeScript参数属性,有
strict:true
tsconfig配置就好了。并且带有参数属性不需要在所有属性上使用装饰器。 - 支持的字段属性,如 Typegoose
- 配置与 mongoose 相同,因此您将很容易熟悉它。
- 支持继承,使编程更自然。
- 模型分析,显示模型名称及其适当的集合名称、应用的配置等。
Usage
用法
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()