TypeScript
概述
在本指南中,您可以了解MongoDB Node.js驱动器的TypeScript功能和限制。TypeScript是一种强类型编程语言,编译成JavaScript。
TypeScript编译器提供实时类型检查。支持TypeScript的代码编辑器可以提供自动完成建议,显示内联文档,并识别与类型相关的错误。
驱动器的所有TypeScript功能都是可选的。使用驱动器编写的所有有效JavaScript代码也是有效的TypeScript代码。
有关更多信息,请参阅TypeScript网站.
功能
如果您使用TypeScript,则可以指定驱动器中某些类的类型。驱动器中所有接受类型参数的类都有默认类型Document
。该Document
接口具有以下定义
interface Document { [key: string]: any; }
所有对象类型都扩展了Document
接口。
有关对象类型的更多信息,请参阅 TypeScript手册。
扩展文档的类型参数
以下类接受所有扩展Document
接口的类型
您可以使用以下方式传递扩展Document
接口的类型参数
1 interface Pet { 2 name: string; 3 age: number; 4 } 5 6 const database = client.db("<your database>"); 7 const collection = database.collection<Pet>("<your collection>");
重要
类型参数中未列出的键接收任何类型
未在指定的类型参数中列出的键接收any
类型。以下代码片段演示了这种行为
1 interface User { 2 email: string; 3 } 4 5 const database = client.db("<your database>"); 6 const myColl = db.collection<User>("<your collection>"); 7 myColl.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 });
您可以通过构造StrictFilter
或StrictUpdateFilter
类型来启用类型检查。
警告
StrictFilter
和StrictUpdateFilter
类型是实验性的,可能会在有效的查询中错误地显示类型错误。
在以下代码示例中,过滤器被分配了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 驱动程序中点符号的限制,请参阅 递归类型和点符号 部分。
处理 _id 字段
MongoDB 建议不要将 _id
指定为模型的一部分。省略 _id
字段可以使模型更通用和可重用,并更准确地模拟应用程序重要的数据。Node 驱动的 TypeScript 集成负责为相关方法返回类型添加 _id
字段。
以下部分提供有关使用 _id
字段进行的写入和读取操作的信息。
插入操作和 _id 字段
您在传递给 Collection
实例的类型参数中指定 _id
字段的方式会影响插入操作的行为。以下表格描述了不同的 _id
字段指定如何影响插入操作
_id 字段类型 | 示例类型 | 插入时是否必需 | 插入时的行为 |
---|---|---|---|
未指定 | 不适用 | 否 | 驱动程序为每个插入的文档创建一个ObjectId值。 |
指定 | { _id: number }; | 是 | 如果您在插入操作中未指定 _id 字段的值,驱动程序将引发错误。 |
指定为可选 | { _id?: number }; | 否 |
如果您必须指定您定义的类型中用于表示集合中文档的_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。
有关本节中讨论的类型、接口和类的更多信息,请参阅以下资源
OptionalId API文档
PkFactory API 文档
ObjectId 源代码
查找方法和 _id 字段
find
和 findOne
方法返回类型包含 _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 手册中的 点符号。
相互递归
当两个类型包含一个属性是另一个类型的类型时,就存在一个 相互递归 类型。您可以通过允许宠物有一个处理程序并定义处理程序有一个宠物来更新 宠物 接口使其相互递归。以下示例引用了相互递归的 Pet
和 Handler
接口
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"});