From 957b97781c324b9fb487783d9f7ae479d220eb00 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 25 Aug 2022 22:57:35 +0200 Subject: [PATCH] fix(NODE-4583): revert nested union type support (#3383) --- src/index.ts | 1 + src/mongo_types.ts | 158 +++++++++--------- .../collection/filterQuery.test-d.ts | 8 +- 3 files changed, 85 insertions(+), 82 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2fa96b8938e..933be8772e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -324,6 +324,7 @@ export type { KeysOfOtherType, MatchKeysAndValues, NestedPaths, + NestedPathsOfType, NonObjectIdLikeDocument, NotAcceptedFields, NumericType, diff --git a/src/mongo_types.ts b/src/mongo_types.ts index 10217082682..124e9ce9e35 100644 --- a/src/mongo_types.ts +++ b/src/mongo_types.ts @@ -68,8 +68,8 @@ export type WithoutId = Omit; export type Filter = | Partial | ({ - [Property in Join, true>, '.'>]?: Condition< - PropertyType, Property, true> + [Property in Join>, '.'>]?: Condition< + PropertyType, Property> >; } & RootFilterOperators>); @@ -261,9 +261,19 @@ export type OnlyFieldsOfType; /** @public */ -export type MatchKeysAndValues = Readonly<{ - [Property in Join, '.'>]?: PropertyType; -}>; +export type MatchKeysAndValues = Readonly< + { + [Property in Join, '.'>]?: PropertyType; + } & { + [Property in `${NestedPathsOfType}.$${`[${string}]` | ''}`]?: ArrayElement< + PropertyType + >; + } & { + [Property in `${NestedPathsOfType[]>}.$${ + | `[${string}]` + | ''}.${string}`]?: any; // Could be further narrowed + } +>; /** @public */ export type AddToSetOperators = { @@ -464,83 +474,75 @@ export type Join = T extends [] : string; /** @public */ -export type PropertyType< - Type, - Property extends string, - AllowToSkipArrayIndex extends boolean -> = Type extends unknown - ? string extends Property - ? Type extends Map +export type PropertyType = string extends Property + ? unknown + : Property extends keyof Type + ? Type[Property] + : Property extends `${number}` + ? Type extends ReadonlyArray + ? ArrayType + : unknown + : Property extends `${infer Key}.${infer Rest}` + ? Key extends `${number}` + ? Type extends ReadonlyArray + ? PropertyType + : unknown + : Key extends keyof Type + ? Type[Key] extends Map ? MapType - : never - : - | (AllowToSkipArrayIndex extends false - ? never - : Type extends ReadonlyArray - ? PropertyType - : never) - | (Property extends keyof Type - ? Type[Property] - : Property extends `${number | `$${'' | `[${string}]`}`}` - ? Type extends ReadonlyArray - ? ArrayType - : never - : Property extends `${infer Key}.${infer Rest}` - ? Key extends `${number | `$${'' | `[${string}]`}`}` - ? Type extends ReadonlyArray - ? PropertyType - : never - : Key extends keyof Type - ? PropertyType - : never - : never) - : never; + : PropertyType + : unknown + : unknown; /** * @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/ */ -export type NestedPaths = Type extends unknown - ? Type extends - | string - | number - | boolean - | Date - | RegExp - | Buffer - | Uint8Array - | ((...args: any[]) => any) - | { _bsontype: string } - ? never - : Type extends ReadonlyArray - ? [ - ...( - | (AllowToSkipArrayIndex extends true ? [] : never) - | [number | `$${'' | `[${string}]`}`] - ), - ...([] | NestedPaths) - ] - : Type extends Map - ? [string] - : Type extends object - ? { - [Key in Extract]: Type[Key] extends Type // type of value extends the parent - ? [Key] - : // for a recursive union type, the child will never extend the parent type. - // but the parent will still extend the child - Type extends Type[Key] - ? [Key] - : Type[Key] extends ReadonlyArray // handling recursive types with arrays - ? Type extends ArrayType // is the type of the parent the same as the type of the array? - ? [Key] // yes, it's a recursive array type - : // for unions, the child type extends the parent - ArrayType extends Type - ? [Key] // we have a recursive array union - : // child is an array, but it's not a recursive array - [Key, ...([] | NestedPaths)] - : // child is not structured the same as the parent - [Key, ...([] | NestedPaths)]; - }[Extract] - : never - : never; +export type NestedPaths = Type extends + | string + | number + | boolean + | Date + | RegExp + | Buffer + | Uint8Array + | ((...args: any[]) => any) + | { _bsontype: string } + ? [] + : Type extends ReadonlyArray + ? [] | [number, ...NestedPaths] + : Type extends Map + ? [string] + : Type extends object + ? { + [Key in Extract]: Type[Key] extends Type // type of value extends the parent + ? [Key] + : // for a recursive union type, the child will never extend the parent type. + // but the parent will still extend the child + Type extends Type[Key] + ? [Key] + : Type[Key] extends ReadonlyArray // handling recursive types with arrays + ? Type extends ArrayType // is the type of the parent the same as the type of the array? + ? [Key] // yes, it's a recursive array type + : // for unions, the child type extends the parent + ArrayType extends Type + ? [Key] // we have a recursive array union + : // child is an array, but it's not a recursive array + [Key, ...NestedPaths] + : // child is not structured the same as the parent + [Key, ...NestedPaths] | [Key]; + }[Extract] + : []; + +/** + * @public + * returns keys (strings) for every path into a schema with a value of type + * https://docs.mongodb.com/manual/tutorial/query-embedded-documents/ + */ +export type NestedPathsOfType = KeysOfAType< + { + [Property in Join, '.'>]: PropertyType; + }, + Type +>; diff --git a/test/types/community/collection/filterQuery.test-d.ts b/test/types/community/collection/filterQuery.test-d.ts index 8f5836712fc..cd719d68045 100644 --- a/test/types/community/collection/filterQuery.test-d.ts +++ b/test/types/community/collection/filterQuery.test-d.ts @@ -407,7 +407,6 @@ nonSpecifiedCollection.find({ } }); -// NODE-4513: improves support for union types and array operators type MyArraySchema = { nested: { array: { a: number; b: boolean }[] }; something: { a: number } | { b: boolean }; @@ -433,9 +432,10 @@ expectAssignable>({ expectAssignable>({ 'something.a': 2 }); -expectError>({ - 'something.a': false -}); +// TODO: NODE-4513: Nested union types don't error in this case. +// expectError>({ +// 'something.a': false +// }); expectAssignable>({ 'something.b': false });