如何实现TypeScript自定义类型校验错误与默认类型?
问题
想要创建泛型类型PojoDocument<T>,用于阻止Mongoose Document从数据访问层(DAL)泄漏,确保返回对象为POJO而非Mongoose的Document类型。
最初尝试通过检查T是否绝对不存在$locals属性(所有Mongoose Document均包含该属性)实现:
// 若`T`包含`$locals`属性,则解析为字符串类型与`T`冲突触发错误;若不存在则返回`T` export type PojoDocument<T> = T & T extends { $locals?: never } ? T : '请通过`.toObject()`或`.lean()`将文档转换为POJO';
但使用时出现类型错误:
class A { async create(dto: CreateUserDto): Promise<PojoDocument<User>> { const result = await this.userModel.create(dto); return result.toObject(); // ^ 类型“LeanDocument<User & Document<any, any, User>>”无法赋值给类型“请通过`.toObject()`或`.lean()`将文档转换为POJO”。 } }
其中LeanDocument<User & Document<any, any, User>>等价于:
interface ResultToObjectType { __v?: any; _id?: any; name: string; }
调用create方法时,返回类型显示为Promise<"请通过.toObject()或.lean()将文档转换为POJO">,说明PojoDocument<T>默认匹配了else分支,而非预期的T。
后续尝试加入__v和_id属性校验,问题依旧:
export type PojoDocument<T> = T extends { $locals?: never; __v?: any; _id?: any } ? T : '请通过`.toObject()`或`.lean()`将文档转换为POJO';
如何正确实现该类型校验?
解决方案
核心问题
原类型的判断逻辑方向错误:T extends { $locals?: never }试图校验T是否不包含$locals属性,但TypeScript的条件类型推断对于无该属性的类型会出现匹配偏差,尤其是当T本身未明确约束$locals时,会误判走错误分支。
正确实现
反转判断逻辑,直接检查T是否包含$locals属性,若包含则返回never触发类型错误,否则返回T:
/** * 确保返回值是POJO而非Mongoose Document类型 * 若出现类型错误,请使用`.toObject()`或`.lean()`将Mongoose文档转换为POJO */ export type PojoDocument<T> = T extends { $locals: any } ? never : T;
为什么这个写法有效?
- Mongoose的
Document类型明确包含$locals属性,因此会被匹配到never分支,直接触发类型错误; .toObject()或.lean()返回的POJO类型不包含$locals,会直接返回T,符合预期。
更严格的校验(可选)
如果需要排除所有Mongoose自带的内部属性(如_doc),可以扩展条件:
export type PojoDocument<T> = T extends { $locals?: any } | { _doc?: any } ? never : T;
内容的提问来源于stack exchange,提问作者EcksDy
相关产品推荐
相关产品推荐

