TypeScript 中的虚拟
¥Virtuals in TypeScript
虚拟 是计算属性:你可以访问水合 Mongoose 文档上的虚拟数据,但虚拟数据不存储在 MongoDB 中。Mongoose 支持自动类型化虚拟,因此你不再需要定义额外的 TypeScript 接口,但你仍然可以这样做。
¥Virtuals are computed properties: you can access virtuals on hydrated Mongoose documents, but virtuals are not stored in MongoDB. Mongoose supports auto typed virtuals so you don't need to define additional typescript interface anymore but you are still able to do so.
自动推断类型
¥Automatically Inferred Types
为了使 mongoose 能够推断虚拟类型,你必须在结构构造函数中定义它们,如下所示:
¥To make mongoose able to infer virtuals type, You have to define them in schema constructor as following:
import { Schema, Model, model } from 'mongoose';
const schema = new Schema(
{
firstName: String,
lastName: String
},
{
virtuals: {
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
}
// virtual setter and options can be defined here as well.
}
}
}
);
请注意,Mongoose 在 InferSchemaType
的返回类型中不包含虚拟值。这是因为 InferSchemaType
返回类似于原始文档接口的值,它表示存储在 MongoDB 中的数据的结构。
¥Note that Mongoose does not include virtuals in the returned type from InferSchemaType
.
That is because InferSchemaType
returns a value similar to the raw document interface, which represents the structure of the data stored in MongoDB.
type User = InferSchemaType<typeof schema>;
const user: User = {};
// Property 'fullName' does not exist on type '{ firstName?: string | undefined; ... }'.
user.fullName;
然而,Mongoose 确实将虚拟添加到模型类型中。
¥However, Mongoose does add the virtuals to the model type.
const UserModel = model('User', schema);
const user = new UserModel({ firstName: 'foo' });
// Works
user.fullName;
// Here's how to get the hydrated document type
type UserDocument = ReturnType<(typeof UserModel)['hydrate']>;
手动设置虚拟类型
¥Set virtuals type manually
你不应该在 TypeScript 文档接口 中定义虚拟。相反,你应该为虚拟定义一个单独的接口,并将该接口传递给 Model
和 Schema
。
¥You shouldn't define virtuals in your TypeScript document interface.
Instead, you should define a separate interface for your virtuals, and pass this interface to Model
and Schema
.
例如,假设你有一个 UserDoc
接口,并且你想要定义一个 fullName
虚拟接口。以下是如何为 fullName
定义单独的 UserVirtuals
接口。
¥For example, suppose you have a UserDoc
interface, and you want to define a fullName
virtual.
Below is how you can define a separate UserVirtuals
interface for fullName
.
import { Schema, Model, model } from 'mongoose';
interface UserDoc {
firstName: string;
lastName: string;
}
interface UserVirtuals {
fullName: string;
}
type UserModelType = Model<UserDoc, {}, {}, UserVirtuals>; // <-- add virtuals here...
const schema = new Schema<UserDoc, UserModelType, {}, {}, UserVirtuals>({ // <-- and here
firstName: String,
lastName: String
});
schema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
覆盖虚拟中 this
的类型
¥Override the Type of this
in Your Virtual
如果虚拟中的 this
值由于某种原因不正确,你始终可以使用 virtual()
函数中的通用参数覆盖它。
¥In case the value of this
in your virtual is incorrect for some reason, you can always override it using the generic parameter in the virtual()
function.
interface MyCustomUserDocumentType {
firstName: string;
lastName: string;
myMethod(): string;
}
schema.virtual<MyCustomUserDocumentType>('fullName').get(function() {
return this.method(); // returns string
});