Skip to content

Commit

Permalink
Merge pull request #15123 from Automattic/vkarpov15/gh-15122
Browse files Browse the repository at this point in the history
types: make BufferToBinary avoid Document instances
  • Loading branch information
vkarpov15 authored Dec 22, 2024
2 parents 8acb95b + 333b7e7 commit bbca321
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 49 deletions.
88 changes: 87 additions & 1 deletion test/types/lean.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Schema, model, Types, InferSchemaType, FlattenMaps } from 'mongoose';
import { Schema, model, Types, InferSchemaType, FlattenMaps, HydratedDocument, Model, Document, PopulatedDoc } from 'mongoose';
import { expectAssignable, expectError, expectType } from 'tsd';

function gh10345() {
Expand Down Expand Up @@ -237,3 +237,89 @@ async function gh15057() {
console.log(item);
};
}

async function gh15122() {
interface IChild {
_id: Types.ObjectId;
name: string;
}

type ChildDocumentOverrides = {};

interface IChildVirtuals {
id: string;
}

type ChildInstance = HydratedDocument<
IChild,
ChildDocumentOverrides & IChildVirtuals
>;

type ChildModelType = Model<
IChild,
{},
ChildDocumentOverrides,
IChildVirtuals,
ChildInstance
>;


interface IParent {
_id: Types.ObjectId;
name: string;
surname: string;
child: PopulatedDoc<Document<Types.ObjectId> & IChild>;
}

type ParentDocumentOverrides = {};

interface IParentVirtuals {
id: string;
fullName: string;
}

type ParentInstance = HydratedDocument<
IParent,
ParentDocumentOverrides & IParentVirtuals
>;

type ParentModelType = Model<
IParent,
{},
ParentDocumentOverrides,
IParentVirtuals,
ParentInstance
>;

const parentSchema = new Schema<IParent, ParentModelType>(
{
name: {
type: String,
required: true,
trim: true
},
surname: {
type: String,
required: true,
trim: true
},
child: {
type: 'ObjectId',
ref: 'Child',
required: true
}
}
);

parentSchema.virtual('fullName').get(function() {
return `${this.name} ${this.surname}`;
});

const Parent = model<IParent, ParentModelType>('Parent', parentSchema);

const testFn = (parent: IParent) => {};
const parentDoc = await Parent.findOne().lean();
if (parentDoc) {
testFn(parentDoc);
}
}
108 changes: 60 additions & 48 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,81 +715,93 @@ declare module 'mongoose' {
*/
export type BufferToBinary<T> = T extends Buffer
? mongodb.Binary
: T extends TreatAsPrimitives
: T extends Document
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Buffer
? mongodb.Binary
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<BufferToBinary<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<SubdocType>
: BufferToBinary<T[K]>;
: T extends TreatAsPrimitives
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Buffer
? mongodb.Binary
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<BufferToBinary<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<BufferToBinary<SubdocType>>
: BufferToBinary<T[K]>;
} : T;

/**
* Converts any Buffer properties into { type: 'buffer', data: [1, 2, 3] } format for JSON serialization
*/
export type BufferToJSON<T> = T extends Buffer
? { type: 'buffer', data: number[] }
: T extends TreatAsPrimitives
: T extends Document
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Buffer
? { type: 'buffer', data: number[] }
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<BufferToBinary<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<SubdocType>
: BufferToBinary<T[K]>;
} : T;
: T extends TreatAsPrimitives
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Buffer
? { type: 'buffer', data: number[] }
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<BufferToBinary<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<SubdocType>
: BufferToBinary<T[K]>;
} : T;

/**
* Converts any ObjectId properties into strings for JSON serialization
*/
export type ObjectIdToString<T> = T extends mongodb.ObjectId
? string
: T extends TreatAsPrimitives
: T extends Document
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends mongodb.ObjectId
? string
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<ObjectIdToString<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
: ObjectIdToString<T[K]>;
} : T;
: T extends TreatAsPrimitives
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends mongodb.ObjectId
? string
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<ObjectIdToString<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<ObjectIdToString<SubdocType>>
: ObjectIdToString<T[K]>;
} : T;

/**
* Converts any Date properties into strings for JSON serialization
*/
export type DateToString<T> = T extends NativeDate
? string
: T extends TreatAsPrimitives
: T extends Document
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends NativeDate
? string
: T[K] extends (NativeDate | null | undefined)
? string | null | undefined
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<DateToString<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<DateToString<SubdocType>>
: DateToString<T[K]>;
} : T;
: T extends TreatAsPrimitives
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends NativeDate
? string
: T[K] extends (NativeDate | null | undefined)
? string | null | undefined
: T[K] extends Types.DocumentArray<infer ItemType>
? Types.DocumentArray<DateToString<ItemType>>
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? HydratedSingleSubdocument<DateToString<SubdocType>>
: DateToString<T[K]>;
} : T;

/**
* Converts any Mongoose subdocuments (single nested or doc arrays) into POJO equivalents
*/
export type SubdocsToPOJOs<T> = T extends TreatAsPrimitives ? T : T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Types.DocumentArray<infer ItemType>
? ItemType[]
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? SubdocType
: SubdocsToPOJOs<T[K]>;
} : T;
export type SubdocsToPOJOs<T> = T extends Document
? T
: T extends TreatAsPrimitives
? T
: T extends Record<string, any> ? {
[K in keyof T]: T[K] extends Types.DocumentArray<infer ItemType>
? ItemType[]
: T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
? SubdocType
: SubdocsToPOJOs<T[K]>;
} : T;

export type JSONSerialized<T> = SubdocsToPOJOs<
FlattenMaps<
Expand Down

0 comments on commit bbca321

Please sign in to comment.