Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(NODE-4423): better type support for nested objects in query & update #3328

Merged
merged 7 commits into from
Jul 20, 2022
2 changes: 1 addition & 1 deletion src/mongo_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ export type NestedPaths<Type> = Type extends
: // child is an array, but it's not a recursive array
[Key, ...NestedPaths<Type[Key]>]
: // child is not structured the same as the parent
[Key, ...NestedPaths<Type[Key]>];
[Key, ...NestedPaths<Type[Key]>] | [Key];
}[Extract<keyof Type, string>]
: [];

Expand Down
3 changes: 3 additions & 0 deletions test/types/community/collection/filterQuery.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ expectNotType<Filter<PetModel>>({ 'regex.dotAll': true });
collectionT.find({ 'meta.updatedAt': new Date() });
collectionT.find({ 'meta.deep.nested.level': 123 });
collectionT.find({ meta: { deep: { nested: { level: 123 } } } }); // no impact on actual nesting
collectionT.find({ 'meta.deep': { nested: { level: 123 } } }); // no impact on actual nesting
coyotte508 marked this conversation as resolved.
Show resolved Hide resolved
collectionT.find({ 'friends.0.name': 'John' });
collectionT.find({ 'playmates.0.name': 'John' });
// supports arrays with primitive types
Expand All @@ -203,6 +204,8 @@ expectNotType<Filter<PetModel>>({ 'friends.0.name': 123 });
expectNotType<Filter<PetModel>>({ 'playmates.0.name': 123 });
expectNotType<Filter<PetModel>>({ 'laps.foo': 'string' });
expectNotType<Filter<PetModel>>({ 'treats.0': 123 });
expectNotType<Filter<PetModel>>({ meta: { deep: { nested: { level: 'string' } } } }); // no impact on actual nesting
coyotte508 marked this conversation as resolved.
Show resolved Hide resolved
expectNotType<Filter<PetModel>>({ 'meta.deep': { nested: { level: 'string' } } }); // no impact on actual nesting
coyotte508 marked this conversation as resolved.
Show resolved Hide resolved

/// it should not accept wrong types for nested document array fields
expectError<Filter<PetModel>>({
Expand Down
13 changes: 13 additions & 0 deletions test/types/community/collection/updateX.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ interface SubTestModel {
field1: string;
field2?: string;
field3?: number;
nestedObject?: {
a: string;
b: string;
};
}

type FruitTypes = 'apple' | 'pear';
Expand All @@ -97,6 +101,7 @@ interface TestModel {
subInterfaceField: SubTestModel;
subInterfaceArray: SubTestModel[];
timestampField: Timestamp;
extras: Record<string, { id: string }>;
}
const collectionTType = db.collection<TestModel>('test.update');

Expand Down Expand Up @@ -201,9 +206,17 @@ expectAssignable<UpdateFilter<TestModel>>({
expectAssignable<UpdateFilter<TestModel>>({ $set: { doubleField: new Double(1.23) } });
expectAssignable<UpdateFilter<TestModel>>({ $set: { int32Field: new Int32(10) } });
expectAssignable<UpdateFilter<TestModel>>({ $set: { longField: Long.fromString('999') } });
expectAssignable<UpdateFilter<TestModel>>({ $set: { extras: { someExtras: { id: 'someId' } } } });
expectAssignable<UpdateFilter<TestModel>>({ $set: { stringField: 'a' } });
expectError(buildUpdateFilter({ $set: { stringField: 123 } }));
expectAssignable<UpdateFilter<TestModel>>({ $set: { 'subInterfaceField.field2': '2' } });
expectAssignable<UpdateFilter<TestModel>>({ $set: { 'subInterfaceField.nestedObject.a': '2' } });
expectAssignable<UpdateFilter<TestModel>>({
$set: { 'subInterfaceField.nestedObject': { a: '1', b: '2' } }
});
expectError<UpdateFilter<TestModel>>({
$set: { 'subInterfaceField.nestedObject': { a: '1' } }
});
coyotte508 marked this conversation as resolved.
Show resolved Hide resolved
expectError(buildUpdateFilter({ $set: { 'subInterfaceField.field2': 2 } }));
expectError(buildUpdateFilter({ $set: { 'unknown.field': null } }));
expectAssignable<UpdateFilter<TestModel>>({ $set: { 'numberArray.$': 40 } });
Expand Down