Schemas in TypeScriptTypeScript中的架构
Mongoose schemas are how you tell Mongoose what your documents look like. Mongoose schemas are separate from TypeScript interfaces, so you need to either define both a document interface and a schema; or rely on Mongoose to automatically infer the type from the schema definition.Mongoose架构是告诉Mongoose您的文档是什么样子的。Mongoose模式与TypeScript接口是分开的,因此您需要同时定义文档接口和架构;或者依靠Mongoose从模式定义中自动推断类型。
Separate document interface definition单独的文档接口定义
import { Schema } from 'mongoose';
// Document interface
interface User {
name: string;
email: string;
avatar?: string;
}
// Schema
const schema = new Schema<User>({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
By default, Mongoose does not check if your document interface lines up with your schema. 默认情况下,Mongoose不会检查您的文档接口是否与您的模式一致。For example, the above code won't throw an error if 例如,如果email
is optional in the document interface, but required
in schema
.email
在文档界面中是可选的,但在schema
中是required
,那么上面的代码不会抛出错误。
Automatic type inference自动类型推断
Mongoose can also automatically infer the document type from your schema definition as follows.Mongoose还可以根据您的模式定义自动推断文档类型,如下所示。
import { Schema, InferSchemaType } from 'mongoose';
// Document interface文档接口
// No need to define TS interface any more.不再需要定义TS接口。
// interface User {
// name: string;
// email: string;
// avatar?: string;
// }
// Schema
const schema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
type User = InferSchemaType<typeof schema>;
// InferSchemaType will determine the type as follows:InferSchemaType将按如下方式确定类型:
// type User = {
// name: string;
// email: string;
// avatar?: string;
// }
// `UserModel` will have `name: string`, etc.
const UserModel = mongoose.model('User', schema);
There are a few caveats for using automatic type inference:对于使用自动类型推断,有一些注意事项:
You need to set您需要在strictNullChecks: true
orstrict: true
in yourtsconfig.json
.tsconfig.json
中设置strictNullChecks:true
或strict:true
。Or, if you're setting flags at the command line,或者,如果要在命令行设置标志,请使用--strictNullChecks
or--strict
.--strictNullChecks
或--strict
。There are known issues with automatic type inference with strict mode disabled.在禁用严格模式的情况下,自动类型推理存在已知问题。You need to define your schema in the您需要在new Schema()
call.new Schema()
调用中定义您的模式。Don't assign your schema definition to a temporary variable.不要将架构定义分配给临时变量。Doing something like执行类似const schemaDefinition = { name: String }; const schema = new Schema(schemaDefinition);
will not work.const schemaDefinition = { name: String }; const schema = new Schema(schemaDefinition);
不会起作用。Mongoose adds如果您在模式中指定了createdAt
andupdatedAt
to your schema if you specify thetimestamps
option in your schema, except if you also specifymethods
,virtuals
, orstatics
.timestamps
选项,Mongoose会将createdAt
和updatedAt
添加到您的模式中,除非您还指定了methods
、virtuals
或statics
。There is a known issue with type inference with timestamps and methods/virtuals/statics options.使用时间戳和方法/virtuals/statics选项进行类型推断存在已知问题。If you use methods, virtuals, and statics, you're responsible for adding如果您使用方法、虚拟和静态,则需要负责将createdAt
andupdatedAt
to your schema definition.createdAt
和updatedAt
添加到模式定义中。
If automatic type inference doesn't work for you, you can always fall back to document interface definitions.如果自动类型推理对您不起作用,那么您总是可以回到文档接口定义。
Generic parameters通用参数
The Mongoose TypeScript中的Mongoose Schema
class in TypeScript has 4 generic parameters:Schema
类有4个通用参数:
DocType
-An interface descibing how the data is saved in MongoDB描述如何在MongoDB中保存数据的界面M
-The Mongoose model type. Can be omitted if there are no query helpers or instance methods to be defined.Mongoose模型类型。如果没有要定义的查询帮助程序或实例方法,则可以省略。default:默认:Model<DocType, any, any>
TInstanceMethods
-An interface containing the methods for the schema.包含架构的方法的接口。- default:
{}
- default:
TQueryHelpers
-An interface containing query helpers defined on the schema.包含在架构上定义的查询帮助程序的接口。Defaults to默认为{}
.{}
。
View TypeScript definition查看TypeScript定义
class Schema<DocType = any, M = Model<DocType, any, any>, TInstanceMethods = {}, TQueryHelpers = {}> extends events.EventEmitter {
// ...
}
The first generic param, 第一个通用参数DocType
, represents the type of documents that Mongoose will store in MongoDB. DocType
表示Mongoose将存储在MongoDB中的文档类型。Mongoose wraps 对于像DocType
in a Mongoose document for cases like the this
parameter to document middleware. this
参数这样的情况,Mongoose将DocType
封装在Mongoose文档中,以文档中间件。For example:例如:
schema.pre('save', function(): void {
console.log(this.name); // TypeScript knows that `this` is a `mongoose.Document & User` by default
});
The second generic param, 第二个通用参数M
, is the model used with the schema. M
是与模式一起使用的模型。Mongoose uses the Mongoose使用模式中定义的M
type in model middleware defined in the schema.M
类型的模型中间件。
The third generic param, 第三个通用参数TInstanceMethods
is used to add types for instance methods defined in the schema.TInstanceMethods
用于为模式中定义的实例方法添加类型。
The 4th param, 第四个参数TQueryHelpers
, is used to add types for chainable query helpers.TQueryHelpers
用于添加可链接查询帮助程序的类型。
Schema vs Interface fields架构与接口字段
Mongoose checks to make sure that every path in your schema is defined in your document interface.Mongoose会进行检查,以确保架构中的每个路径都在文档接口中定义。
For example, the below code will fail to compile because 例如,下面的代码将无法编译,因为email
is a path in the schema, but not in the DocType
interface.email
是架构中的一个路径,而不是DocType
接口中的路径。
import { Schema, Model } from 'mongoose';
interface User {
name: string;
email: string;
avatar?: string;
}
// Object literal may only specify known properties, but 'emaill' does not exist in type ...对象文字只能指定已知的属性,但类型中不存在“emaill”……
// Did you mean to write 'email'?你是想写“电子邮件”吗?
const schema = new Schema<User>({
name: { type: String, required: true },
emaill: { type: String, required: true },
avatar: String
});
However, Mongoose does not check for paths that exist in the document interface, but not in the schema. 然而,Mongoose不会检查文档接口中存在的路径,但不会检查模式中存在的。For example, the below code compiles.例如,以下代码进行编译。
import { Schema, Model } from 'mongoose';
interface User {
name: string;
email: string;
avatar?: string;
createdAt: number;
}
const schema = new Schema<User, Model<User>>({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
This is because Mongoose has numerous features that add paths to your schema that should be included in the 这是因为Mongoose有许多功能,可以向您的模式添加路径,这些路径应该包含在DocType
interface without you explicitly putting these paths in the Schema()
constructor. DocType
接口中,而无需将这些路径显式地放入schema()
构造函数中。For example, timestamps and plugins.例如,时间戳和插件。
Arrays数组
When you define an array in a document interface, we recommend using Mongoose's 当您在文档界面中定义数组时,我们建议将Mongoose的Types.Array
type for primitive arrays or Types.DocumentArray
for arrays of documents.Types.Array
类型用于基元数组,或将Types.DocumentArray
用于文档数组。
import { Schema, Model, Types } from 'mongoose';
interface BlogPost {
_id: Types.ObjectId;
title: string;
}
interface User {
tags: Types.Array<string>;
blogPosts: Types.DocumentArray<BlogPost>;
}
const schema = new Schema<User, Model<User>>({
tags: [String],
blogPosts: [{ title: String }]
});
Using 在处理默认值时,使用Types.DocumentArray
is helpful when dealing with defaults. Types.DocumentArray
很有帮助。For example, 例如,BlogPost
has an _id
property that Mongoose will set by default. If you use Types.DocumentArray
in the above case, you'll be able to push()
a subdocument without an _id
.BlogPost
有一个_id
属性,Mongoose将默认设置该属性。如果在上述情况下使用Types.DocumentArray
,则可以push()
不带_id
的子文档。
const user = new User({ blogPosts: [] });
user.blogPosts.push({ title: 'test' }); // Would not work if you did `blogPosts: BlogPost[]`