Skip to content

Commit

Permalink
feat(NODE-3875): support recursive schema types (#3433)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken committed Oct 13, 2022
1 parent a7dab96 commit 26bce4a
Show file tree
Hide file tree
Showing 7 changed files with 772 additions and 198 deletions.
43 changes: 26 additions & 17 deletions src/mongo_types.ts
Expand Up @@ -68,7 +68,7 @@ export type WithoutId<TSchema> = Omit<TSchema, '_id'>;
export type Filter<TSchema> =
| Partial<TSchema>
| ({
[Property in Join<NestedPaths<WithId<TSchema>>, '.'>]?: Condition<
[Property in Join<NestedPaths<WithId<TSchema>, []>, '.'>]?: Condition<
PropertyType<WithId<TSchema>, Property>
>;
} & RootFilterOperators<WithId<TSchema>>);
Expand Down Expand Up @@ -263,7 +263,7 @@ export type OnlyFieldsOfType<TSchema, FieldType = any, AssignableType = FieldTyp
/** @public */
export type MatchKeysAndValues<TSchema> = Readonly<
{
[Property in Join<NestedPaths<TSchema>, '.'>]?: PropertyType<TSchema, Property>;
[Property in Join<NestedPaths<TSchema, []>, '.'>]?: PropertyType<TSchema, Property>;
} & {
[Property in `${NestedPathsOfType<TSchema, any[]>}.$${`[${string}]` | ''}`]?: ArrayElement<
PropertyType<TSchema, Property extends `${infer Key}.$${string}` ? Key : never>
Expand All @@ -272,7 +272,7 @@ export type MatchKeysAndValues<TSchema> = Readonly<
[Property in `${NestedPathsOfType<TSchema, Record<string, any>[]>}.$${
| `[${string}]`
| ''}.${string}`]?: any; // Could be further narrowed
}
} & Document
>;

/** @public */
Expand Down Expand Up @@ -498,20 +498,29 @@ export type PropertyType<Type, Property extends string> = string extends Propert
* @public
* returns tuple of strings (keys to be joined on '.') that represent every path into a schema
* https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
*
* @remarks
* Through testing we determined that a depth of 8 is safe for the typescript compiler
* and provides reasonable compilation times. This number is otherwise not special and
* should be changed if issues are found with this level of checking. Beyond this
* depth any helpers that make use of NestedPaths should devolve to not asserting any
* type safety on the input.
*/
export type NestedPaths<Type> = Type extends
| string
| number
| boolean
| Date
| RegExp
| Buffer
| Uint8Array
| ((...args: any[]) => any)
| { _bsontype: string }
export type NestedPaths<Type, Depth extends number[]> = Depth['length'] extends 8
? []
: Type extends
| string
| number
| boolean
| Date
| RegExp
| Buffer
| Uint8Array
| ((...args: any[]) => any)
| { _bsontype: string }
? []
: Type extends ReadonlyArray<infer ArrayType>
? [] | [number, ...NestedPaths<ArrayType>]
? [] | [number, ...NestedPaths<ArrayType, [...Depth, 1]>]
: Type extends Map<string, any>
? [string]
: Type extends object
Expand All @@ -529,9 +538,9 @@ export type NestedPaths<Type> = Type extends
ArrayType extends Type
? [Key] // we have a recursive array union
: // child is an array, but it's not a recursive array
[Key, ...NestedPaths<Type[Key]>]
[Key, ...NestedPaths<Type[Key], [...Depth, 1]>]
: // child is not structured the same as the parent
[Key, ...NestedPaths<Type[Key]>] | [Key];
[Key, ...NestedPaths<Type[Key], [...Depth, 1]>] | [Key];
}[Extract<keyof Type, string>]
: [];

Expand All @@ -542,7 +551,7 @@ export type NestedPaths<Type> = Type extends
*/
export type NestedPathsOfType<TSchema, Type> = KeysOfAType<
{
[Property in Join<NestedPaths<TSchema>, '.'>]: PropertyType<TSchema, Property>;
[Property in Join<NestedPaths<TSchema, []>, '.'>]: PropertyType<TSchema, Property>;
},
Type
>;
4 changes: 2 additions & 2 deletions test/types/basic_schema.test-d.ts
@@ -1,6 +1,6 @@
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';

import { ObjectId } from '../../src/bson';
import { Document, ObjectId } from '../../src/bson';
import { Collection } from '../../src/collection';
import { Db } from '../../src/db';
import { MongoClient } from '../../src/mongo_client';
Expand All @@ -20,7 +20,7 @@ expectType<Collection<ACounterWithId>>(new Collection<ACounterWithId>(db, ''));
////////////////////////////////////////////////////////////////////////////////////////////////////
// Simple Schema that does not define an _id
// With _id
type InsertOneArgOf<S> = Parameters<Collection<S>['insertOne']>[0];
type InsertOneArgOf<S extends Document> = Parameters<Collection<S>['insertOne']>[0];
expectAssignable<InsertOneArgOf<ACounter>>({ _id: new ObjectId(), a: 3 });
// Without _id
expectAssignable<InsertOneArgOf<ACounter>>({ a: 3 });
Expand Down
175 changes: 0 additions & 175 deletions test/types/community/collection/findX-recursive-types.test-d.ts

This file was deleted.

0 comments on commit 26bce4a

Please sign in to comment.