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

[Types] Query with dotted paths #12064

Open
2 tasks done
hasezoey opened this issue Jul 7, 2022 · 7 comments
Open
2 tasks done

[Types] Query with dotted paths #12064

hasezoey opened this issue Jul 7, 2022 · 7 comments
Labels
enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature

Comments

@hasezoey
Copy link
Collaborator

hasezoey commented Jul 7, 2022

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

Currently Query's do not have dotted path suggestions, but would be possible with current typescript.

See This Stackoverflow Answer

TL;DR:
a simple example of what the stackoverflow answer has does (note: the types are directly copied from the answer)

interface Test {
  somenested?: {
    path: string;
  };
  somethingelse?: string;
}

type PathsToStringProps<T> = T extends string
  ? []
  : {
      [K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>];
    }[Extract<keyof T, string>];

type Join<T extends string[], D extends string> = T extends []
  ? never
  : T extends [infer F]
  ? F
  : T extends [infer F, ...infer R]
  ? F extends string
    ? `${F}${D}${Join<Extract<R, string[]>, D>}`
    : never
  : string;

function onlyDotted(input: Join<PathsToStringProps<Test>, '.'>) {
  console.log('input', input);
}

onlyDotted('somenested.path'); // works, with suggestions
onlyDotted('somethingelse'); // also works, with suggestions
onlyDotted('ERROR'); // error, property does not exist

Disclaimer: i am by no means a expert at complex typescript types, i also have no clue about the effect on types performance this would bring

@vkarpov15 vkarpov15 added this to the 6.4.5 milestone Jul 8, 2022
@IslandRhythms IslandRhythms added the enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature label Jul 11, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.4.5, 6.4.6, 6.4.7 Jul 15, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.4.7, 6.4.9 Jul 25, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.5.2, 6.5.3 Aug 9, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.5.3, 6.5.5 Aug 23, 2022
@hasezoey
Copy link
Collaborator Author

hasezoey commented Sep 5, 2022

just noticed that #10180 is a similar Issue, should that one be closed in favor of this (or vise versa)?

@vkarpov15 vkarpov15 modified the milestones: 6.5.5, 6.5.6 Sep 7, 2022
@vkarpov15
Copy link
Collaborator

I'm really wary of doing this because I imagine it'll be horrifically slow and cause a lot of impossible to debug "Type instantiation is excessively deep and possibly infinite" type errors.

I've tried adding the following to query.d.ts:

  type PathsToStringProps<T> = T extends string
    ? []
    : {
        [K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>];
      }[Extract<keyof T, string>];

  type Join<T extends string[], D extends string> = T extends []
    ? never
    : T extends [infer F]
    ? F
    : T extends [infer F, ...infer R]
    ? F extends string
      ? `${F}${D}${Join<Extract<R, string[]>, D>}`
      : never
    : string;

  type _FilterQuery<T> = {
    [P in keyof T]?: Condition<T[P]>;
  } & RootQuerySelector<T>;

  /**
   * Filter query to select the documents that match the query
   * @example
   * ```js
   * { age: { $gte: 30 } }
   * ```
   */
  type FilterQuery<T> = _FilterQuery<T> & Join<PathsToStringProps<T>, '.'>;

And I'm getting the following:

../mongoose/types/query.d.ts:34:48 - error TS2321: Excessive stack depth comparing types 'PathsToStringProps<T>' and 'string[]'.

34   type FilterQuery<T> = _FilterQuery<T> & Join<PathsToStringProps<T>, '.'>;
                                                  ~~~~~~~~~~~~~~~~~~~~~


Found 1 error in ../mongoose/types/query.d.ts:34

Haven't been able to find a workaround. Any suggestions @hasezoey ?

@vkarpov15 vkarpov15 modified the milestones: 6.5.6, 6.x Unprioritized Sep 14, 2022
@hasezoey
Copy link
Collaborator Author

Haven't been able to find a workaround. Any suggestions @hasezoey ?

no i dont have any solutions, and yes it would cause may "too deep" errors, because that is basically what this approach is "meant" to do, the only ways i could see are:

  • limit depth somehow (maybe even dynamically), if at all possible with current typescript
  • dont do it at all in types

i had just noted it here because it was now possible and mongoose already uses it in places at runtime but didnt have it in types

@PCOffline
Copy link

PCOffline commented Mar 23, 2023

I stumbled upon this myself a couple of days ago and came up with a solution (whose performance is questionable but perhaps a bit better):
https://tsplay.dev/wO2xRW

@PCOffline
Copy link

Hey, I came up with a solution, as seen above ^
This solution is very performant – according to my tests, the compilation and emit of the type file and a file that uses this type take around 20ms.
The implementation makes FilterQuery more strict by overriding the ApplyBasicQueryCasting to avoid the unnecessary any in the union.
The two main types are DeepNestedAccess and RecursiveFieldsOfObject.
DeepNestedAccess is a way to access elements in objects and arrays via dot-notation strings (e.g. 'array.0.field' will actually access the 'field' element in the first element of the array).
RecursiveFieldsOfObject gets all the paths and nested paths of an object, including arrays.

@PCOffline
Copy link

PCOffline commented Aug 19, 2023

I'll provide some screenshots and sources for my tests and open a PR in a sec

@vkarpov15
Copy link
Collaborator

Unfortunately @PCOffline 's solution doesn't work too well, I tried it for #14615 but unfortunately results in an infinite type instantiation error in automatic schema inference.

../mongoose/types/index.d.ts:90:3 - error TS2589: Type instantiation is excessively deep and possibly infinite.

 90   HydratedDocument<
      ~~~~~~~~~~~~~~~~~
 91   InferSchemaType<TSchema>,
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 93   ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 94   >,
    ~~~

Realistically, I don't think we can support this unless TypeScript has some sort of way to limit the depth of the recursion or avoiding infinite recursion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature
Projects
None yet
Development

No branches or pull requests

4 participants