尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Mongoose TypeScript 查询指南:Query 泛型、lean() 与 transform() 的类型推断实践

Mongoose TypeScript 查询指南:Query 泛型、lean() 与 transform() 的类型推断实践 Mongoose TypeScript 查询指南Query 泛型、lean() 与 transform() 的类型推断实践【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongooseMongoose 的 Query 类是一个可链式调用的查询构建器代表一条 MongoDB 查询。本文聚焦 TypeScript 场景下 Query 泛型参数的完整含义、lean()的返回类型推导机制以及lean()与transform()在查询链中的调用顺序对类型推断的关键影响帮助你在实际项目中写出类型安全、可复用且无隐式any的查询代码。Query 类链式查询构建器与 Promise 化在 Mongoose 中当你在模型上调用find()、findOne()、updateOne()、findOneAndUpdate()等方法时返回的并不是文档数组或文档本身而是一个Query实例。这个实例支持链式调用.select()、.where()、.populate()、.lean()等并且带有.then()方法返回一个 Promise因此可以直接使用await等待结果const projects await ProjectModel.find().lean();在运行时层面Query.prototype.lean与Query.prototype.transform的实现位于 lib/query.js 与 lib/query.js而类型层面Query类的完整声明位于 types/query.d.ts。实际应用中模型方法返回的往往是QueryWithHelpers——它是Query与查询助手Query Helpers类型的交叉类型定义见 types/query.d.tstype QueryWithHelpers... QueryResultType, DocType, THelpers, RawDocType, QueryOp, TDocOverrides THelpers;这意味着当你使用查询助手Query Helpers时助手方法也会被类型系统正确识别与普通 Query 方法无缝衔接。Query 的六个泛型参数在 TypeScript 中Query类接受以下泛型参数定义见 types/query.d.tsclass Query ResultType, // The type of the result of the query, like DocType[] DocType, // The hydrated document type of the querys associated model THelpers {}, // Query helpers RawDocType unknown, // The lean document type of the querys associated model QueryOp find, // The operation that will be executed, like find, findOne, updateOne, etc. TDocOverrides Recordstring, never // Methods and virtuals on the hydrated document 逐一说明各参数的实际含义泛型参数默认值含义ResultType必填第一位查询结果的类型例如find()对应DocType[]findOne()对应DocType \| nullDocType必填第二位查询关联模型的「水合hydrated」文档类型即经过 Mongoose 处理、带有实例方法后的文档THelpers{}查询助手Query Helpers的类型集合RawDocTypeunknown关联模型的「lean」文档类型即未经水合的纯数据形态QueryOpfind将要执行的操作如find、findOne、updateOne等TDocOverridesRecordstring, never水合文档上的方法和虚拟属性virtuals覆盖类型其中QueryOp并非普通字符串它在类型系统中参与条件类型推导。例如 types/query.d.ts 定义了QueryOpThatReturnsDocument联合类型GetLeanResultType会根据QueryOp是否属于返回文档的操作find | findOne | findOneAndUpdate | findOneAndReplace | findOneAndDelete来决定 lean 结果的类型形态type QueryOpThatReturnsDocument find | findOne | findOneAndUpdate | findOneAndReplace | findOneAndDelete; type GetLeanResultTypeRawDocType, ResultType, QueryOp QueryOp extends QueryOpThatReturnsDocument ? (ResultType extends any[] ? Default__vRequire_idRawDocType[] : Default__vRequire_idRawDocType) : ResultType;也就是说当查询操作返回文档如find/findOne时lean 结果由RawDocType推导而来并自动补全_id与__v字段而对于updateOne、deleteMany等不返回文档的操作lean 结果保持ResultType原样。在业务代码中通常不需要手动填写这些泛型参数——当你用modelDocType(Project, schema)创建模型后模型方法的返回类型会自动推断。查询助手的完整类型化用法可以参考类型测试 test/types/queries.test.tsconst query: mongoose.QueryR, T, object, T, TQueryOp ...; const content await query.lean().orFail().exec();在 TypeScript 中使用 lean()lean()方法指示 Mongoose 跳过对结果文档的「水合」hydrate参见 Model.hydrate 相关实现直接返回纯 JavaScript 对象从而让查询更快、内存占用更低。类型层面types/query.d.ts 为lean()提供了多组重载无参调用lean()将结果类型重写为基于RawDocType的 lean 形态例如find().lean()返回Default__vRequire_idRawDocType[]传lean(true)或lean(LeanOptions)行为同无参调用传lean(false)显式关闭 lean结果类型回退为水合的DocType数组场景为DocType[]显式指定leanLeanResultType()允许你手动覆盖 lean 结果的元素类型默认LeanResultType RawDocType。这种设计让「是否 lean」成为类型层面的可区分信息。例如在类型测试 test/types/queries.test.ts 中通过条件类型Options[lean] extends true ? PickBlog, ... : HydratedDocument...同一个findOne封装方法在传入{ lean: true }选项时返回值类型会自动从水合文档切换为纯数据对象findOneProjection extends ProjectionFieldsBlog, Options extends QueryOptionsBlog( filter: QueryFiltermongoose.WithLevel1NestedPathsBlog, projection: Projection, options: Options ): Promise Options[lean] extends true ? PickBlog, Extractkeyof Projection, keyof Blog | null : HydratedDocumentPickBlog, Extractkeyof Projection, keyof Blog | null { return this.blogModel.findOne(filter, projection, options); } // options 传 { lean: true } 时blog 被推断为纯对象类型而非 HydratedDocument const blog await blogRepository.findOne({ title: test }, { content: 1 }, { lean: true });lean() 与 transform() 的调用顺序transform()用于对查询结果执行一次映射转换其类型签名见 types/query.d.ts为transformMappedType(fn: (doc: ResultType) MappedType): QueryWithHelpersMappedType, DocType, THelpers, RawDocType, QueryOp, TDocOverrides;也就是说transform会把查询的ResultType重写为回调函数的返回类型MappedType。这正是 TypeScript 场景下lean()与transform()顺序问题的根源lean()只能识别「查询返回文档」或「查询返回文档数组」这两种形态并据此从RawDocType推导 lean 类型而transform()会把ResultType改造成任意自定义形状如Map、Record等此时lean()的类型逻辑无法预知这一新形态可能导致推断出错误的类型。因此官方建议在 TypeScript 中始终先调用lean()再调用transform()// 正确做法把 lean() 放在 transform() 之前。 // 因为 transform 会把查询的 ResultType 改造成 lean() 无法识别的形状。 const result await ProjectModel .find() .lean() .transform((docs) new Map(docs.map((doc) [doc._id.toString(), doc]))); // 错误示范先 transform 再 lean类型推断容易出错。 const result await ProjectModel .find() .transform((docs) new Map(docs.map((doc) [doc._id.toString(), doc]))) .lean();上面的正确示例中transform的回调参数docs已被推导为 lean 后的纯对象数组元素含_id、__v因此doc._id.toString()可以安全调用而错误示范里transform先执行时回调参数仍是水合文档类型随后lean()面对已被改写的ResultType无法保证正确推导。实战建议把lean()尽量提前如果在使用lean()时遇到类型推断异常例如结果类型变成了unknown或丢失了字段优先尝试把lean()移到查询链的更靠前位置让类型系统在transform()、populate()等方法改写ResultType之前就确定 lean 形态。区分水合文档与 lean 对象lean 结果不带实例方法如doc.save()、虚拟属性类型上对应RawDocType而非DocType如果你的代码依赖实例方法不要对 lean 结果调用。利用lean(false)与显式泛型在需要动态切换 lean 开关的封装函数中可借助Options[lean] extends true条件类型让返回类型自动跟随选项需要完全自定义 lean 元素类型时可使用leanMyLeanType()显式指定。查阅测试用例加深理解类型层面的预期行为可以在 test/types/queries.test.ts 中验证例如 select 投影后的可选字段推断test/types/queries.test.ts运行时行为可对照 lib/query.js 中lean与 lib/query.js 中transform的实现。【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表