Docs 菜单
Docs 主页
/ / /
Node.js
/

Typescript

在此页面上

  • Overview
  • 功能
  • 用于扩展文档的类型参数
  • 任何类型的类型参数
  • 类型安全性和点符号
  • 使用 _id 字段
  • 插入操作和 _id 字段
  • 查找方法和 _id 字段
  • 已知限制
  • 递归类型和点符号
  • 相互递归

在本指南中,您可了解 MongoDB Node.js 驱动程序的 TypeScript 功能和限制。TypeScript 是一种可编译为 JavaScript 的强类型编程语言。

TypeScript 编译器提供实时类型检查。支持 TypeScript 的代码编辑器可以提供自动补全建议、以内联方式显示文档并识别与类型相关的错误。

该驱动程序的所有 TypeScript 功能都是可选的。使用该驱动程序编写的所有有效 JavaScript 代码也会是有效的 TypeScript 代码。

有关更多信息,请参阅 TypeScript 网站。

如果您使用 TypeScript,则可以为驱动程序中的某些类指定一个类型。驱动程序中接受类型参数的所有类都具有默认类型 DocumentDocument 接口具有以下定义:

interface Document {
[key: string]: any;
}

所有对象类型都会扩展 Document 接口。

有关对象类型的更多信息,请参阅 TypeScript 手册

以下类接受扩展 Document 接口的所有类型:

  • Collection

  • 变更流

您可以传递扩展 Document 接口的类型参数,如下所示:

1interface Pet {
2 name: string;
3 age: number;
4}
5
6const database = client.db("<your database>");
7const collection = database.collection<Pet>("<your collection>");

重要

不在类型参数中的键接收任何类型

指定类型参数中未列出的键会接收 any 类型。以下代码片段演示了此行为:

1interface User {
2 email: string;
3}
4
5const database = client.db("<your database>");
6const myColl = db.collection<User>("<your collection>");
7myColl.find({ age: "Accepts any type!" });

以下类接受所有类型参数:

您可以在查找多个文档用法示例中找到一个代码片段,该片段展示了如何为FindCursor类指定类型。

从版本 5.0 开始,默认情况下,Node.js 驱动程序不为搜索以点符号表示的字段的操作提供类型安全性。点符号是一种可用于导航嵌套 JSON 对象的语法。当您构造要传递给查询的过滤器时,即使您为以点符号表示的字段指定了错误的类型值,驱动程序也不会引发类型错误。

以下代码片段定义了包括一个 classification 字段的 ClassificationPet 接口,您可以通过该字段指定狗和猫的属名和颜色:

interface ClassificationPet {
name: string;
age: number;
classification: { genus: "Canis" | "Felis"; color: string };
}

对于以下代码示例,驱动程序不会引发类型错误,即使 classification.color 的值是布尔值而不是字符串:

await myColl.findOneAndDelete({ "classification.color": false });

您可以将筛选器构造为 StrictFilterStrictUpdateFilter 类型,以启用类型检查。

警告

StrictFilterStrictUpdateFilter 类型为实验性类型,且可能会在有效查询中显示本应不存在的类型错误。

在以下代码示例中,为该筛选器分配了 StrictFilter 类型。对于此筛选器类型,Node.js 驱动程序会报告类型错误,因为 classification.color 的值为布尔值而不是字符串。

const filterPredicate: StrictFilter<ClassificationPet> = { "classification.color": false };
await myColl.findOneAndDelete(filterPredicate);

以下示例会将 StrictUpdateFilter 类型分配给更新筛选器。Node.js 驱动程序会报告类型错误,因为 classification.color 的值为布尔值而不是字符串。

const updateFilter: StrictUpdateFilter<ClassificationPet> = { $set: { "classification.color": false } }
await pets.updateOne({}, updateFilter);

要查询集合或使用包含变量的键执行其他操作,您必须在指定键时使用 as const 断言。如果输入类型正确,此机制允许您的代码成功编译。

以下代码片段定义了 ClassificationPet 接口和 Mealtime 接口。ClassificationPet 包括一个 mealtimes 字段,而该字段包含一组 Mealtime 接口,且每个接口均包括一个 time 字段:

interface ClassificationPet {
name: string;
mealtimes: Mealtime[];
}
interface Mealtime{
time: string;
amount: number;
}

以下代码片段对 ClassificationPet 文档集合执行查找和更新操作。该操作会更新索引 1Mealtime 实例的嵌套 time 字段。索引位置由变量 mealCounter 指定:

const mealCounter = 1;
await myColl.findOneAndUpdate(
{ name: "Lassie" },
{ $set: { [`mealtimes.${mealCounter}.time` as const]: '4:00 PM' } },
);

要了解有关点表示法的更多信息,请参阅 MongoDB 手册中的点表示法

要详细了解 Node.js 驱动程序中点表示法的限制,请参阅递归类型和点表示法部分。

MongoDB 不建议将 _id 指定为模型的一部分。省略 _id 字段会使模型更加通用和可重用,并且可以更准确地模拟对应用程序重要的数据。Node 驱动程序的 TypeScript 集成负责将 _id 字段添加到相关方法的返回类型。

以下各节提供 _id 字段的写入和读取操作的相关信息。

您在传递给 Collection 实例的类型参数中指定 _id 字段的方式会影响插入操作的行为。下表描述了不同的 _id 字段规范如何影响插入操作:

_id 字段类型
示例类型
插入时是否为必填项
插入时的行为
Unspecified
Not applicable
No
The driver creates an ObjectId value for each inserted document.
Specified
{ _id: number };
Yes
If you do not specify a value for the _id field in an insert operation, the driver raises an error.
Specified as optional
{ _id?: number };
No
If you do not specify the _id field in an insert operation, the driver adds an _id field value generated by the primary key factory.

如果您必须在定义的类型中指定 _id 字段以表示集合中的文档,但不想在插入操作中指定 _id 字段的值,则在创建集合时使用 OptionalId 辅助程序类型。OptionalId类型接受类型形参作为参数,并返回带有可选_id字段的类型。

以下代码段定义了 IdPet 接口,其中包括 _id 字段的类型:

interface IdPet {
_id: ObjectId;
name: string;
age: number;
}

以下代码使用前面的接口以及 OptionalId 类型来插入文档,无需指定 _id 字段的值:

const database = client.db("<your database>");
const collection = db.collection<OptionalId<IdPet>>("<your collection>");
myColl.insertOne({
name: "Spot",
age: 2
});

要了解有关 _id 字段的更多信息,请参阅 MongoDB 手册中的 _id 字段

要了解有关本节中讨论的类型、接口和类的更多信息,请参阅以下资源:

Collection 类的 findfindOne 方法在其返回类型中包含 _id 字段。驱动程序根据您传递给 Collection 实例的类型参数推断返回的 _id 字段的类型。

如果传递给 Collection 实例的类型参数在其模式中包含 _id 字段,驱动程序则会推断从该方法返回的 _id 字段属于模式中指定的类型。

但是,如果传递给Collection实例的类型参数在其架构中不包含 _id 字段,则驱动程序会推断从该方法返回的 _id 字段的类型为 ObjectId

提示

传递给 Collection 的类型参数仅影响从该方法返回的字段的类型推断。驱动程序不会将该字段转换为指定类型。类型参数模式中每个字段的类型应与集合中相应字段的类型匹配。

以下代码使用 Pet 接口返回一个文档,该文档中 _id 的类型被推断为 ObjectId

const database = client.db("<your database>");
const collection = db.collection<Pet>("<your collection>");
const document = await myColl.findOne({
name: "Spot",
});
const id : ObjectId = document._id;

以下代码使用 IdNumberPet 接口返回一个文档,其中 _id 被推断为 number 类型:

interface IdNumberPet {
_id: number;
name: string;
age: number;
}
const database = client.db("<your database>");
const collection = db.collection<IdNumberPet>("<your collection>");
const document = await myColl.findOne({
name: "Spot",
});
const id : number = document._id;

重要

投射

如果您在 find 方法中指定了投影,则应将类型参数传递给 find 方法,以反映所投影文档的结构。如果没有类型参数,TypeScript 无法在编译时检查您是否安全地使用了所投影的文档。

下面的例子将形象说明此行为。以下代码片段将通过类型检查,但会在运行时引发错误:

const doc = await myColl.findOne(
{},
{ projection: { _id: 0, name: 1 } }
);
console.log(doc._id.generationTime);

要在编译时捕获此错误,请将不包含 _id 字段的类型参数传递给 find 方法:

interface ProjectedDocument {
name: string
}
const doc = await myColl.findOne<ProjectedDocument>(
{},
{ projection: { _id: 0, name: 1 } }
);
// Compile time error: Property '_id' does not exist on type 'ProjectedDocument'.
console.log(doc._id.generationTime);

要查看包含应用了投影的 find 方法的可运行 TypeScript 示例,请参阅查找文档页面。

要了解本节中讨论的类和方法的更多信息,请参阅以下 API 文档:

了解 Node.js 驱动程序的以下 TypeScript 特定限制:

Node.js 驱动程序无法在通过点符号引用的递归类型的嵌套实例中提供类型安全性。

递归类型是一种引用自身的类型。您可以允许宠物拥有自己的宠物,以便将 Pet 接口更新为递归类型。以下是递归类型的 Pet 接口:

interface RecursivePet {
pet?: RecursivePet;
name: string;
age: number;
}

注意

深度限制

当对使用点表示法的键进行类型检查时,Node.js 驱动程序不会遍历嵌套递归类型,以避免触及 TypeScript 的递归深度限制。

以下代码段使用点表示法引用了类型不正确的 RecursivePet 接口的嵌套实例,但是 TypeScript 编译器没有引发类型错误:

database
.collection<RecursivePet>("<your collection>")
.findOne({ "pet.age": "Spot" });

以下代码段引用类型不正确的 RecursivePet 接口的顶级实例,并引发类型错误:

database
.collection<RecursivePet>("<your collection>")
.findOne({ pet: "Spot" });

以上代码片段引发的错误如下:

index.ts(19,59): error TS2769: No overload matches this call.
The last overload gave the following error.
Type 'string' is not assignable to type 'Condition<Pet>'.

如果一定要在递归类型的嵌套实例中保证类型安全,那么您必须在不使用点表示法的情况下编写查询或更新。

要了解有关点表示法的更多信息,请参阅 MongoDB 手册中的点表示法

当两种类型互相包含对方类型的属性时,就称为互递归类型。您可以允许宠物拥有处理程序并定义处理程序来拥有宠物,以便将 Pet 接口更新为互递归。下面的示例引用了互递归的 PetHandler 接口:

interface Pet {
handler?: Handler;
name: string;
age: number;
}
interface Handler {
pet: Pet;
name: string;
}

Node.js 驱动程序为通过点符号引用的相互递归类型提供类型安全性,深度可达 8。以下代码段将 string 分配给 number 并引发类型错误,因为引用属性的深度为 4:

database
.collection<Pet>("<your collection>")
.findOne({'handler.pet.handler.pet.age': "four"});

以上代码片段引发的错误如下:

index.ts(19,59): error TS2769: No overload matches this call.
The last overload gave the following error.
Type 'string' is not assignable to type 'Condition<number> | undefined'.

当深度大于或等于 8 时,TypeScript 会编译您的代码,但不再对其进行类型检查。以下代码将 string 赋予给 number 属性,但不会导致编译错误,因为引用属性的深度为 10:

database
.collection<Pet>("<your collection>")
.findOne({'handler.pet.handler.pet.handler.pet.handler.pet.handler.pet.age': "four"});

后退

时间序列