I've tried to create a PojoDocument<T>
generic type to guard from leaking mongoose documents out of our DALs.
My goal was to make sure the returned object is not a mongoose Document
, but a POJO(PojoDocument
).
I've done it by checking that T
can never have the $locals
property, as it's found on all documents:
// If `T` has `$locals` property, then resolve the string as the type, which will clash with `T` and show as an error.
// So when `$locals` property not found, `T` will be returned as the type.
export type PojoDocument<T> = T & T extends { $locals?: never }
? T
: 'Please convert the document to a POJO via `.toObject()` or .lean()`';
My usage of the type looks like this:
class A {
...
async create(dto: CreateUserDto): Promise<PojoDocument<User>> {
const result = await this.userModel.create(dto);
return result.toObject();
// ^ Type 'LeanDocument<User & Document<any, any, User>>' is not assignable to type '"Please convert the document to a POJO via `.toObject()` or .lean()`"'.
}
}
The LeanDocument<User & Document<any, any, User>>
type is equals to:
interface ResultToObjectType {
__v?: any;
_id?: any;
name: string;
}
Also, when I check what is returned by the create
method where it's used, I get this:
Promise<"Please convert the document to a POJO via `.toObject()` or .lean()`">
This means PojoDocument<T>
defaults to the "else" condition instead of the T
that was provided for it.
I've tried modifying the type to include __v
and _id
, thinking that's the reason it doesn't consider the type as being extended by $locals?: never
, as these properties don't exist on T
, but it didn't change anything:
export type PojoDocument<T> = T extends { $locals?: never; __v?: any; _id?: any }
? T
: 'Please convert the document to a POJO via `.toObject()` or .lean()`';
Any ideas how this check can be achieved?
Edit #1
I've added a minimal repro playground.