Skip to content

Commit 5337da9

Browse files
committed
Revert "fix: Strict Projection Object Typing (#13993)"
This reverts commit 6a88762.
1 parent 7dafe5b commit 5337da9

File tree

4 files changed

+6
-128
lines changed

4 files changed

+6
-128
lines changed

test/types/queries.test.ts

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import {
1515
QuerySelector,
1616
InferSchemaType,
1717
ProjectionFields,
18-
QueryOptions,
19-
ProjectionType
18+
QueryOptions
2019
} from 'mongoose';
2120
import { ModifyResult, ObjectId } from 'mongodb';
2221
import { expectAssignable, expectError, expectNotAssignable, expectType } from 'tsd';
@@ -55,9 +54,6 @@ interface ISubdoc {
5554
myId?: Types.ObjectId;
5655
id?: number;
5756
tags?: string[];
58-
profiles: {
59-
name?: string
60-
}
6157
}
6258

6359
interface ITest {
@@ -158,37 +154,6 @@ const p1: Record<string, number> = Test.find().projection('age docs.id');
158154
const p2: Record<string, number> | null = Test.find().projection();
159155
const p3: null = Test.find().projection(null);
160156

161-
expectError(Test.find({ }, { name: 'ss' })); // Only 0 and 1 are allowed
162-
expectError(Test.find({ }, { name: 3 })); // Only 0 and 1 are allowed
163-
expectError(Test.find({ }, { name: true, age: false, endDate: true, tags: 1 })); // Exclusion in a inclusion projection is not allowed
164-
expectError(Test.find({ }, { name: true, age: false, endDate: true })); // Inclusion in a exclusion projection is not allowed
165-
expectError(Test.find({ }, { name: false, age: false, tags: false, child: { name: false }, docs: { myId: false, id: true } })); // Inclusion in a exclusion projection is not allowed in nested objects and arrays
166-
expectError(Test.find({ }, { tags: { something: 1 } })); // array of strings or numbers should only be allowed to be a boolean or 1 and 0
167-
Test.find({}, { name: true, age: true, endDate: true, tags: 1, child: { name: true }, docs: { myId: true, id: true } }); // This should be allowed
168-
Test.find({}, { name: 1, age: 1, endDate: 1, tags: 1, child: { name: 1 }, docs: { myId: 1, id: 1 } }); // This should be allowed
169-
Test.find({}, { _id: 0, name: 1, age: 1, endDate: 1, tags: 1, child: 1, docs: 1 }); // _id is an exception and should be allowed to be excluded
170-
Test.find({}, { name: 0, age: 0, endDate: 0, tags: 0, child: 0, docs: 0 }); // This should be allowed
171-
Test.find({}, { name: 0, age: 0, endDate: 0, tags: 0, child: { name: 0 }, docs: { myId: 0, id: 0 } }); // This should be allowed
172-
Test.find({}, { name: 1, age: 1, _id: 0 }); // This should be allowed since _id is an exception
173-
Test.find({}, { someOtherField: 1 }); // This should be allowed since it's not a field in the schema
174-
expectError(Test.find({}, { name: { $slice: 1 } })); // $slice should only be allowed on arrays
175-
Test.find({}, { tags: { $slice: 1 } }); // $slice should be allowed on arrays
176-
Test.find({}, { tags: { $slice: [1, 2] } }); // $slice with the format of [ <number to skip>, <number to return> ] should also be allowed on arrays
177-
expectError(Test.find({}, { age: { $elemMatch: {} } })); // $elemMatch should not be allowed on non arrays
178-
Test.find({}, { tags: { $elemMatch: {} } }); // $elemMatch should be allowed on arrays
179-
expectError(Test.find({}, { tags: { $slice: 1, $elemMatch: {} } })); // $elemMatch and $slice should not be allowed together
180-
Test.find({}, { age: 1, tags: { $slice: 5 } }); // $slice should be allowed in inclusion projection
181-
Test.find({}, { age: 0, tags: { $slice: 5 } }); // $slice should be allowed in exclusion projection
182-
Test.find({}, { age: 1, tags: { $elemMatch: {} } }); // $elemMatch should be allowed in inclusion projection
183-
Test.find({}, { age: 0, tags: { $elemMatch: {} } }); // $elemMatch should be allowed in exclusion projection
184-
expectError(Test.find({}, { 'docs.id': 11 })); // Dot notation should be allowed and does not accept any
185-
expectError(Test.find({}, { docs: { id: '1' } })); // Dot notation should be able to use a combination with objects
186-
Test.find({}, { docs: { id: false } }); // Dot notation should be allowed with valid values - should correctly handle arrays
187-
Test.find({}, { docs: { id: true } }); // Dot notation should be allowed with valid values - should correctly handle arrays
188-
Test.find({}, { child: 1 }); // Dot notation should be able to use a combination with objects
189-
Test.find({}, { 'docs.profiles': { name: 1 } }); // should support a combination of dot notation and objects
190-
expectError(Test.find({}, { 'docs.profiles': { name: 'aa' } })); // should support a combination of dot notation and objects
191-
expectError(Test.find({}, { endDate: { toString: 1 } }));
192157
// Sorting
193158
Test.find().sort();
194159
Test.find().sort('-name');

test/types/schema.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ function pluginOptions() {
760760
}
761761

762762
const schema = new Schema({});
763-
expectType<typeof schema>(schema.plugin(pluginFunction)); // test that chaining would be possible
763+
expectType<Schema<any>>(schema.plugin(pluginFunction)); // test that chaining would be possible
764764

765765
// could not add strict tests that the parameters are inferred correctly, because i dont know how this would be done in tsd
766766

types/index.d.ts

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -592,96 +592,9 @@ declare module 'mongoose' {
592592

593593
export type ReturnsNewDoc = { new: true } | { returnOriginal: false } | { returnDocument: 'after' };
594594

595-
type ArrayOperators = { $slice: number | [number, number]; $elemMatch?: undefined } | { $elemMatch: object; $slice?: undefined };
596-
type Projector<T, Element> = T extends Array<infer U>
597-
? Projector<U, Element> | ArrayOperators
598-
: T extends object
599-
? {
600-
[K in keyof T]?: T[K] extends object ? Projector<T[K], Element> | Element : Element;
601-
}
602-
: Element;
603-
type _IDType = { _id?: boolean | 1 | 0 };
604-
type InclusionProjection<T> = NestedPartial<Projector<NestedRequired<DotKeys<T>>, true | 1> & _IDType>;
605-
type ExclusionProjection<T> = NestedPartial<Projector<NestedRequired<DotKeys<T>>, false | 0> & _IDType>;
606-
type ProjectionUnion<T> = InclusionProjection<T> | ExclusionProjection<T>;
607-
608-
type NestedRequired<T> = T extends Array<infer U>
609-
? Array<NestedRequired<U>>
610-
: T extends object
611-
? {
612-
[K in keyof T]-?: NestedRequired<T[K]>;
613-
}
614-
: T;
615-
type NestedPartial<T> = T extends Array<infer U>
616-
? Array<NestedPartial<U>>
617-
: T extends object
618-
? {
619-
[K in keyof T]?: NestedPartial<T[K]>;
620-
}
621-
: T;
622-
623-
/** https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object/58436959#58436959 for dot nested implementation it is used and then modified */
624-
type DotPrefix<T extends string> = T extends '' ? '' : `.${T}`;
625-
626-
/** https://stackoverflow.com/questions/75419012/how-do-i-limit-the-level-of-nesting-on-a-recursive-type-in-typescript */
627-
type Length<T extends unknown[]> = T extends { length: infer L } ? L : never;
628-
type BuildTuple<L extends number, T extends unknown[] = []> = T extends { length: L } ? T : BuildTuple<L, [...T, unknown]>;
629-
type MinusOne<N extends number> = BuildTuple<N> extends [...infer U, unknown] ? Length<U> : never;
595+
export type ProjectionElementType = number | string;
596+
export type ProjectionType<T> = { [P in keyof T]?: ProjectionElementType } | AnyObject | string;
630597

631-
/**
632-
* Generates a union from dot path in object
633-
* We have to give it the Depth to prevent infinite recursion and also prevent slow compilation when the object is too much nested
634-
*/
635-
type DotNestedKeys<T, Depth extends number> = (
636-
Depth extends 0
637-
? never
638-
: T extends object
639-
? { [K in Exclude<keyof T, symbol>]: `${K}` | `${K}${DotPrefix<DotNestedKeys<T[K], MinusOne<Depth>>>}` }[Exclude<keyof T, symbol>]
640-
: ''
641-
) extends infer D
642-
? Extract<D, string>
643-
: never;
644-
type FindDottedPathType<T, Path extends string> = Path extends `${infer K}.${infer R}`
645-
? K extends keyof T
646-
? FindDottedPathType<T[K] extends Array<infer U> ? U : T[K], R>
647-
: never
648-
: Path extends keyof T
649-
? T[Path]
650-
: never;
651-
type ExtractNestedArrayElement<T> = T extends (infer U)[]
652-
? ExtractNestedArrayElement<U>
653-
: T extends object
654-
? { [K in keyof T]: ExtractNestedArrayElement<T[K]> }
655-
: T;
656-
type DotnotationMaximumDepth = 4;
657-
/**
658-
* Create dot path for nested objects
659-
* It creates dot notation for arrays similar to mongodb. For example { a: { c: { b: number}[] }[] } => 'a.c.b': number, 'a.c': { b: number }[]
660-
*/
661-
type DotKeys<DocType> = {
662-
[key in DotNestedKeys<ExtractNestedArrayElement<DocType>, DotnotationMaximumDepth>]?: FindDottedPathType<NestedRequired<DocType>, key>;
663-
};
664-
665-
/**
666-
* This types are equivalent to primary types
667-
*/
668-
type SpecialTypes = DateSchemaDefinition | Date | globalThis.Date | DateConstructor | Types.Buffer | Types.Decimal128 | Types.Buffer | BooleanSchemaDefinition | NumberSchemaDefinition;
669-
type Replacer<T> = T extends SpecialTypes ? string : T;
670-
/**
671-
* Date type is like a Primitiv type for us and we do not want to project something inside it.
672-
* ObjectId is also similar.
673-
*/
674-
type ReplaceSpecialTypes<T> = T extends SpecialTypes
675-
? Replacer<T>
676-
: T extends Array<infer U>
677-
? Array<ReplaceSpecialTypes<U>>
678-
: T extends object
679-
? {
680-
[K in keyof T]?: ReplaceSpecialTypes<T[K]>;
681-
}
682-
: Replacer<T>;
683-
684-
export type ProjectionType<T> = (ProjectionUnion<ReplaceSpecialTypes<T>> & AnyObject) | string | ((...agrs: any) => any);
685598
export type SortValues = SortOrder;
686599

687600
export type SortOrder = -1 | 1 | 'asc' | 'ascending' | 'desc' | 'descending';

types/query.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ declare module 'mongoose' {
102102
updatedAt?: boolean;
103103
}
104104

105-
interface QueryOptions<DocType = any> extends
105+
interface QueryOptions<DocType = unknown> extends
106106
PopulateOption,
107107
SessionOption {
108108
arrayFilters?: { [key: string]: any }[];
@@ -129,7 +129,7 @@ declare module 'mongoose' {
129129
new?: boolean;
130130

131131
overwriteDiscriminatorKey?: boolean;
132-
projection?: ProjectionType<any>;
132+
projection?: ProjectionType<DocType>;
133133
/**
134134
* if true, returns the full ModifyResult rather than just the document
135135
*/

0 commit comments

Comments
 (0)