Skip to content

Commit 26bce4a

Browse files
authored
feat(NODE-3875): support recursive schema types (#3433)
1 parent a7dab96 commit 26bce4a

7 files changed

+772
-198
lines changed

src/mongo_types.ts

+26-17
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export type WithoutId<TSchema> = Omit<TSchema, '_id'>;
6868
export type Filter<TSchema> =
6969
| Partial<TSchema>
7070
| ({
71-
[Property in Join<NestedPaths<WithId<TSchema>>, '.'>]?: Condition<
71+
[Property in Join<NestedPaths<WithId<TSchema>, []>, '.'>]?: Condition<
7272
PropertyType<WithId<TSchema>, Property>
7373
>;
7474
} & RootFilterOperators<WithId<TSchema>>);
@@ -263,7 +263,7 @@ export type OnlyFieldsOfType<TSchema, FieldType = any, AssignableType = FieldTyp
263263
/** @public */
264264
export type MatchKeysAndValues<TSchema> = Readonly<
265265
{
266-
[Property in Join<NestedPaths<TSchema>, '.'>]?: PropertyType<TSchema, Property>;
266+
[Property in Join<NestedPaths<TSchema, []>, '.'>]?: PropertyType<TSchema, Property>;
267267
} & {
268268
[Property in `${NestedPathsOfType<TSchema, any[]>}.$${`[${string}]` | ''}`]?: ArrayElement<
269269
PropertyType<TSchema, Property extends `${infer Key}.$${string}` ? Key : never>
@@ -272,7 +272,7 @@ export type MatchKeysAndValues<TSchema> = Readonly<
272272
[Property in `${NestedPathsOfType<TSchema, Record<string, any>[]>}.$${
273273
| `[${string}]`
274274
| ''}.${string}`]?: any; // Could be further narrowed
275-
}
275+
} & Document
276276
>;
277277

278278
/** @public */
@@ -498,20 +498,29 @@ export type PropertyType<Type, Property extends string> = string extends Propert
498498
* @public
499499
* returns tuple of strings (keys to be joined on '.') that represent every path into a schema
500500
* https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
501+
*
502+
* @remarks
503+
* Through testing we determined that a depth of 8 is safe for the typescript compiler
504+
* and provides reasonable compilation times. This number is otherwise not special and
505+
* should be changed if issues are found with this level of checking. Beyond this
506+
* depth any helpers that make use of NestedPaths should devolve to not asserting any
507+
* type safety on the input.
501508
*/
502-
export type NestedPaths<Type> = Type extends
503-
| string
504-
| number
505-
| boolean
506-
| Date
507-
| RegExp
508-
| Buffer
509-
| Uint8Array
510-
| ((...args: any[]) => any)
511-
| { _bsontype: string }
509+
export type NestedPaths<Type, Depth extends number[]> = Depth['length'] extends 8
510+
? []
511+
: Type extends
512+
| string
513+
| number
514+
| boolean
515+
| Date
516+
| RegExp
517+
| Buffer
518+
| Uint8Array
519+
| ((...args: any[]) => any)
520+
| { _bsontype: string }
512521
? []
513522
: Type extends ReadonlyArray<infer ArrayType>
514-
? [] | [number, ...NestedPaths<ArrayType>]
523+
? [] | [number, ...NestedPaths<ArrayType, [...Depth, 1]>]
515524
: Type extends Map<string, any>
516525
? [string]
517526
: Type extends object
@@ -529,9 +538,9 @@ export type NestedPaths<Type> = Type extends
529538
ArrayType extends Type
530539
? [Key] // we have a recursive array union
531540
: // child is an array, but it's not a recursive array
532-
[Key, ...NestedPaths<Type[Key]>]
541+
[Key, ...NestedPaths<Type[Key], [...Depth, 1]>]
533542
: // child is not structured the same as the parent
534-
[Key, ...NestedPaths<Type[Key]>] | [Key];
543+
[Key, ...NestedPaths<Type[Key], [...Depth, 1]>] | [Key];
535544
}[Extract<keyof Type, string>]
536545
: [];
537546

@@ -542,7 +551,7 @@ export type NestedPaths<Type> = Type extends
542551
*/
543552
export type NestedPathsOfType<TSchema, Type> = KeysOfAType<
544553
{
545-
[Property in Join<NestedPaths<TSchema>, '.'>]: PropertyType<TSchema, Property>;
554+
[Property in Join<NestedPaths<TSchema, []>, '.'>]: PropertyType<TSchema, Property>;
546555
},
547556
Type
548557
>;

test/types/basic_schema.test-d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
22

3-
import { ObjectId } from '../../src/bson';
3+
import { Document, ObjectId } from '../../src/bson';
44
import { Collection } from '../../src/collection';
55
import { Db } from '../../src/db';
66
import { MongoClient } from '../../src/mongo_client';
@@ -20,7 +20,7 @@ expectType<Collection<ACounterWithId>>(new Collection<ACounterWithId>(db, ''));
2020
////////////////////////////////////////////////////////////////////////////////////////////////////
2121
// Simple Schema that does not define an _id
2222
// With _id
23-
type InsertOneArgOf<S> = Parameters<Collection<S>['insertOne']>[0];
23+
type InsertOneArgOf<S extends Document> = Parameters<Collection<S>['insertOne']>[0];
2424
expectAssignable<InsertOneArgOf<ACounter>>({ _id: new ObjectId(), a: 3 });
2525
// Without _id
2626
expectAssignable<InsertOneArgOf<ACounter>>({ a: 3 });

test/types/community/collection/findX-recursive-types.test-d.ts

-175
This file was deleted.

0 commit comments

Comments
 (0)