文档菜单
文档首页
/ / /
Node.js 驱动程序
/

TypeScript

本页内容

  • 概述
  • 特性
  • 扩展文档的类型参数
  • 任何类型的类型参数
  • 类型安全和点表示法
  • 与_id字段一起工作
  • 插入操作和_id字段
  • 查找方法和_id字段
  • 已知限制
  • 递归类型和点表示法
  • 相互递归

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

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

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

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

如果您使用TypeScript,则可以指定驱动器中某些类的类型。驱动器中所有接受类型参数的类都有默认类型Document。该Document接口具有以下定义

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

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

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

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

  • Collection

  • ChangeStream

您可以使用以下方式传递扩展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对象的语法。当您构造一个过滤器以传递给查询时,即使您为点符号表示的字段指定了错误类型的值,驱动程序也不会引发类型错误。

以下代码片段定义了ClassificationPet接口,该接口包含一个classification字段,该字段允许您指定狗和猫的属和颜色。

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类型分配给更新过滤器。由于classification.color的值是布尔值而不是字符串,Node.js驱动程序报告了类型错误。

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 文档集合执行查找和更新操作。该操作更新 Mealtime 实例中索引位置为 1 的嵌套 time 字段。索引位置由变量 mealCounter 指定。

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

要了解关于点符号的更多信息,请参阅 MongoDB 手册中的 点符号

要了解 Node.js 驱动程序中点符号的限制,请参阅 递归类型和点符号 部分。

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

以下部分提供有关使用 _id 字段进行的写入和读取操作的信息。

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

_id 字段类型
示例类型
插入时是否必需
插入时的行为
未指定
不适用
驱动程序为每个插入的文档创建一个ObjectId值。
指定
{ _id: number };
如果您在插入操作中未指定_id字段的值,驱动程序将引发错误。
指定为可选
{ _id?: number };
如果您在插入操作中未指定_id字段,驱动程序将添加由主键工厂生成的_id字段值。

如果您必须指定您定义的类型中用于表示集合中文档的_id字段为必需的,但您不想在插入操作中指定_id字段的值,请在创建集合时使用OptionalId辅助类型。`OptionalId`类型接受一个类型参数作为参数,并返回具有可选_id字段的该类型。

以下代码片段定义了包含_id字段类型的IdPet接口。

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手册中的The _id Field

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

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

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

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

提示

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

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

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 接口返回一个推断为 number 类型的 _id 的文档

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

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

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 接口

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 手册中的 点符号

当两个类型包含一个属性是另一个类型的类型时,就存在一个 相互递归 类型。您可以通过允许宠物有一个处理程序并定义处理程序有一个宠物来更新 宠物 接口使其相互递归。以下示例引用了相互递归的 PetHandler 接口

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

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

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会编译您的代码,但不再进行类型检查。以下代码将一个字符串赋值给一个数字属性,但不会导致编译错误,因为引用的属性位于深度为10的位置

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

返回

时间序列