Skip to content

Commit

Permalink
fix(index.d.ts): simplify UpdateQuery to avoid "excessively deep and …
Browse files Browse the repository at this point in the history
…possibly infinite" errors with `extends Document` and `any`

Fix #10617
  • Loading branch information
vkarpov15 committed Sep 1, 2021
1 parent 3ee32b1 commit e1d4aa4
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 72 deletions.
85 changes: 15 additions & 70 deletions index.d.ts
Expand Up @@ -2561,87 +2561,32 @@ declare module 'mongoose' {
$sort?: SortValues | Record<string, SortValues>;
};

type SetFields<TSchema> = ({
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any> | undefined>]?:
| Unpacked<TSchema[key]>
| AddToSetOperators<Unpacked<TSchema[key]>[]>;
} &
NotAcceptedFields<TSchema, ReadonlyArray<any> | undefined>) & {
readonly [key: string]: AddToSetOperators<any> | any;
};

type PushOperator<TSchema> = ({
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
| Unpacked<TSchema[key]>
| ArrayOperator<Unpacked<TSchema[key]>[]>;
} &
NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
readonly [key: string]: ArrayOperator<any> | any;
};

type ObjectQuerySelector<T> = T extends object ? { [key in keyof T]?: QuerySelector<T[key]> } : QuerySelector<T>;

type PullOperator<TSchema> = {
[key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
| Partial<Unpacked<TSchema[key]>>
| ObjectQuerySelector<Unpacked<TSchema[key]>>
// Doesn't look like TypeScript has good support for creating an
// object containing dotted keys:
// https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object
| any;
} | any; // Because TS doesn't have good support for creating an object with dotted keys, including `.$.` re: #10075

type PullAllOperator<TSchema> = ({
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?: TSchema[key];
} &
NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
readonly [key: string]: any[];
type OnlyFieldsOfType<TSchema, FieldType = any, AssignableType = FieldType> = {
[key in keyof TSchema]?: [Extract<TSchema[key], FieldType>] extends [never] ? never : AssignableType;
};

type KeysOfAType<TSchema, Type> = {
[key in keyof TSchema]: NonNullable<TSchema[key]> extends Type ? key : never;
}[keyof TSchema];
type KeysOfOtherType<TSchema, Type> = {
[key in keyof TSchema]: NonNullable<TSchema[key]> extends Type ? never : key;
}[keyof TSchema];

type AcceptedFields<TSchema, FieldType, AssignableType> = {
readonly [key in KeysOfAType<TSchema, FieldType>]?: AssignableType;
};

/** It avoid uses fields of non Type */
type NotAcceptedFields<TSchema, FieldType> = {
readonly [key in KeysOfOtherType<TSchema, FieldType>]?: never;
};

type OnlyFieldsOfType<TSchema, FieldType = any, AssignableType = FieldType> = AcceptedFields<
TSchema,
FieldType,
AssignableType
> &
NotAcceptedFields<TSchema, FieldType> &
DotAndArrayNotation<AssignableType>;

type NumericTypes = number | Decimal128 | mongodb.Double | mongodb.Int32 | mongodb.Long;

type _UpdateQuery<TSchema> = {
/** @see https://docs.mongodb.com/manual/reference/operator/update-field/ */
$currentDate?: OnlyFieldsOfType<TSchema, NativeDate, true | { $type: 'date' | 'timestamp' }>;
$inc?: OnlyFieldsOfType<TSchema, NumericTypes | undefined>;
$min?: MatchKeysAndValues<TSchema>;
$max?: MatchKeysAndValues<TSchema>;
$mul?: OnlyFieldsOfType<TSchema, NumericTypes | undefined>;
$currentDate?: OnlyFieldsOfType<TSchema, NativeDate, true | { $type: 'date' | 'timestamp' }> & AnyObject;
$inc?: OnlyFieldsOfType<TSchema, NumericTypes | undefined> & AnyObject;
$min?: OnlyFieldsOfType<TSchema, any, any> & AnyObject;
$max?: OnlyFieldsOfType<TSchema, any, any> & AnyObject;
$mul?: OnlyFieldsOfType<TSchema, NumericTypes | undefined> & AnyObject;
$rename?: { [key: string]: string };
$set?: MatchKeysAndValues<TSchema>;
$setOnInsert?: MatchKeysAndValues<TSchema>;
$unset?: OnlyFieldsOfType<TSchema, any, any>;
$set?: OnlyFieldsOfType<TSchema, any, any> & AnyObject;
$setOnInsert?: OnlyFieldsOfType<TSchema, any, any> & AnyObject;
$unset?: OnlyFieldsOfType<TSchema, any, any> & AnyObject;

/** @see https://docs.mongodb.com/manual/reference/operator/update-array/ */
$addToSet?: SetFields<TSchema>;
$pop?: OnlyFieldsOfType<TSchema, ReadonlyArray<any>, 1 | -1>;
$pull?: PullOperator<TSchema>;
$push?: PushOperator<TSchema>;
$pullAll?: PullAllOperator<TSchema>;
$addToSet?: OnlyFieldsOfType<TSchema, any[], any> & AnyObject;
$pop?: OnlyFieldsOfType<TSchema, ReadonlyArray<any>, 1 | -1> & AnyObject;
$pull?: OnlyFieldsOfType<TSchema, ReadonlyArray<any>, any> & AnyObject;
$push?: OnlyFieldsOfType<TSchema, ReadonlyArray<any>, any> & AnyObject;
$pullAll?: OnlyFieldsOfType<TSchema, ReadonlyArray<any>, any> & AnyObject;

/** @see https://docs.mongodb.com/manual/reference/operator/update-bitwise/ */
$bit?: {
Expand Down
15 changes: 15 additions & 0 deletions test/typescript/queries.ts
Expand Up @@ -127,4 +127,19 @@ function eachAsync(): void {
Test.find().cursor().eachAsync((doc: ITest) => console.log(doc.name));

Test.find().cursor().eachAsync((docs: ITest[]) => console.log(docs[0].name), { batchSize: 2 });
}

async function gh10617(): Promise<void> {
interface IDBModel extends Document {
date: Date; // date created
_tags: any[];
}

const schema = new Schema({
date: { type: Date, default: Date.now }, // date created
_tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }]
});

const DBModel: Model<IDBModel> = model<IDBModel>('Meep', schema);
await DBModel.findOne({});
}
4 changes: 2 additions & 2 deletions test/typescript/virtuals.ts
Expand Up @@ -51,8 +51,8 @@ petSchema.virtual('owner', {
}
});

const Person = model('Person', personSchema);
const Pet = model('Pet', petSchema);
const Person = model<IPerson>('Person', personSchema);
const Pet = model<IPet>('Pet', petSchema);

(async() => {
const person = await Person.create({ _id: 1, firstName: 'John', lastName: 'Wick' });
Expand Down

0 comments on commit e1d4aa4

Please sign in to comment.