如何修复类型'IUser & { _id: any; }'不存在属性'_doc'的TS报错
Mongoose 查询返回的是包装后的 Document 实例对象,_doc 是实例上存储原始文档字段的内部属性,Mongoose 官方公开的 TypeScript 类型定义中没有将该内部属性纳入 Document 类型的公开声明,直接访问会触发类型报错。
手动在 IUser 接口中添加 _doc 属性定义不会对 User 数据库模型、实际运行逻辑造成任何影响——TypeScript 类型仅在编译阶段做校验,不会侵入运行时的 Schema 定义或数据库存储结构,但这种写法会将 Mongoose 内部属性暴露到业务类型定义中,且 _doc 不属于公开稳定API,版本迭代时可能出现结构变动,存在兼容隐患。
方案1:使用官方toObject()方法(通用场景)
Mongoose Document 实例自带公开的 toObject() 方法,作用就是将包装后的文档实例转换为普通JavaScript对象,自动剥离Mongoose内置的实例方法、内部属性,返回值类型会自动匹配IUser中定义的字段,无需修改现有接口定义。
修改后的resolver代码:
import User from '../models/User' import { GraphQLResolveInfo } from 'graphql' const resolvers = { Query: { users (parent: any, args: any, context: any, info: GraphQLResolveInfo) { return User.find() .then (userList => { return userList.map(r => r.toObject()) }) } } }
如果需要隐藏默认返回的__v版本键、转换_id格式,可以传入配置项:
r.toObject({ versionKey: false, getters: true })
方案2:查询时添加lean()选项(列表查询最优)
如果查询出的文档不需要调用save()等Mongoose实例方法,可以在查询链中调用.lean(),Mongoose会直接返回普通JS对象数组,不会生成Document实例,从根源上规避内部属性访问问题,同时查询性能比返回Document实例高3-5倍,非常适合GraphQL接口只返回数据的场景。
修改后的resolver代码:
import User from '../models/User' import { GraphQLResolveInfo } from 'graphql' const resolvers = { Query: { users (parent: any, args: any, context: any, info: GraphQLResolveInfo) { // 加lean()后直接返回普通对象,无需额外解构 return User.find().lean() } } }
方案3:临时类型断言(不推荐)
如果一定要直接解构_doc,不需要修改IUser接口定义,在访问时做类型断言即可,不会产生运行时影响:
return user.map (r => ({ ...(r._doc as IUser) }))
该方案仅适合临时快速修复,长期维护不建议使用,因为内部属性无稳定API承诺,版本升级可能出现非预期问题。
内容的提问来源于stack exchange,提问作者yvirdi

